rtc-mxc: remove unnecessary clock source for rtc subsystem
[safe/jmp/linux-2.6] / fs / jffs2 / write.c
index d5c7819..c819eb0 100644 (file)
@@ -1,27 +1,25 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.97 2005/11/07 11:14:42 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/crc32.h>
-#include <linux/slab.h>
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
 #include "nodelist.h"
 #include "compr.h"
 
 
-int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
+int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                      uint32_t mode, struct jffs2_raw_inode *ri)
 {
        struct jffs2_inode_cache *ic;
 
@@ -33,10 +31,9 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
        memset(ic, 0, sizeof(*ic));
 
        f->inocache = ic;
-       f->inocache->nlink = 1;
+       f->inocache->pino_nlink = 1; /* Will be overwritten shortly for directories */
        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));
@@ -57,12 +54,14 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
 /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
    write it to the flash, link it into the existing inode/fragment list */
 
-struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
+struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                                          struct jffs2_raw_inode *ri, const unsigned char *data,
+                                          uint32_t datalen, int alloc_mode)
 
 {
-       struct jffs2_raw_node_ref *raw;
        struct jffs2_full_dnode *fn;
        size_t retlen;
+       uint32_t flash_ofs;
        struct kvec vecs[2];
        int ret;
        int retried = 0;
@@ -78,34 +77,21 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
        vecs[1].iov_base = (unsigned char *)data;
        vecs[1].iov_len = datalen;
 
-       jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
-
        if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
                printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
        }
-       raw = jffs2_alloc_raw_node_ref();
-       if (!raw)
-               return ERR_PTR(-ENOMEM);
 
        fn = jffs2_alloc_full_dnode();
-       if (!fn) {
-               jffs2_free_raw_node_ref(raw);
+       if (!fn)
                return ERR_PTR(-ENOMEM);
-       }
-
-       fn->ofs = je32_to_cpu(ri->offset);
-       fn->size = je32_to_cpu(ri->dsize);
-       fn->frags = 0;
 
        /* check number of valid vecs */
        if (!datalen || !data)
                cnt = 1;
  retry:
-       fn->raw = raw;
+       flash_ofs = write_ofs(c);
 
-       raw->flash_offset = flash_ofs;
-       raw->__totlen = PAD(sizeof(*ri)+datalen);
-       raw->next_phys = NULL;
+       jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
 
        if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
                BUG_ON(!retried);
@@ -125,22 +111,16 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
 
                /* Mark the space as dirtied */
                if (retlen) {
-                       /* Doesn't belong to any inode */
-                       raw->next_in_ino = NULL;
-
                        /* Don't change raw->size to match retlen. We may have
                           written the node header already, and only the data will
                           seem corrupted, in which case the scan would skip over
                           any node we write before the original intended end of
                           this node */
-                       raw->flash_offset |= REF_OBSOLETE;
-                       jffs2_add_physical_node_ref(c, raw);
-                       jffs2_mark_node_obsolete(c, raw);
+                       jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*ri)+datalen), NULL);
                } else {
-                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
-                       jffs2_free_raw_node_ref(raw);
+                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs);
                }
-               if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
+               if (!retried && alloc_mode != ALLOC_NORETRY) {
                        /* Try to reallocate space and retry */
                        uint32_t dummy;
                        struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
@@ -153,19 +133,20 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
                        jffs2_dbg_acct_paranoia_check(c, jeb);
 
                        if (alloc_mode == ALLOC_GC) {
-                               ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs,
-                                                       &dummy, JFFS2_SUMMARY_INODE_SIZE);
+                               ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &dummy,
+                                                            JFFS2_SUMMARY_INODE_SIZE);
                        } else {
                                /* Locking pain */
-                               up(&f->sem);
+                               mutex_unlock(&f->sem);
                                jffs2_complete_reservation(c);
 
-                               ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs,
-                                                       &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE);
-                               down(&f->sem);
+                               ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &dummy,
+                                                         alloc_mode, JFFS2_SUMMARY_INODE_SIZE);
+                               mutex_lock(&f->sem);
                        }
 
                        if (!ret) {
+                               flash_ofs = write_ofs(c);
                                D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
 
                                jffs2_dbg_acct_sanity_check(c,jeb);
@@ -174,7 +155,6 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
                                goto retry;
                        }
                        D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
-                       jffs2_free_raw_node_ref(raw);
                }
                /* Release the full_dnode which is now useless, and return */
                jffs2_free_full_dnode(fn);
@@ -188,20 +168,23 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
        if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
            ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
              (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) ==  je32_to_cpu(ri->isize)))) {
-               raw->flash_offset |= REF_PRISTINE;
+               flash_ofs |= REF_PRISTINE;
        } else {
-               raw->flash_offset |= REF_NORMAL;
+               flash_ofs |= REF_NORMAL;
        }
-       jffs2_add_physical_node_ref(c, raw);
-
-       /* Link into per-inode list */
-       spin_lock(&c->erase_completion_lock);
-       raw->next_in_ino = f->inocache->nodes;
-       f->inocache->nodes = raw;
-       spin_unlock(&c->erase_completion_lock);
+       fn->raw = jffs2_add_physical_node_ref(c, flash_ofs, PAD(sizeof(*ri)+datalen), f->inocache);
+       if (IS_ERR(fn->raw)) {
+               void *hold_err = fn->raw;
+               /* Release the full_dnode which is now useless, and return */
+               jffs2_free_full_dnode(fn);
+               return ERR_CAST(hold_err);
+       }
+       fn->ofs = je32_to_cpu(ri->offset);
+       fn->size = je32_to_cpu(ri->dsize);
+       fn->frags = 0;
 
        D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
-                 flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize),
+                 flash_ofs & ~3, flash_ofs & 3, je32_to_cpu(ri->dsize),
                  je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
                  je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
 
@@ -212,12 +195,14 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
        return fn;
 }
 
-struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
+struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                                            struct jffs2_raw_dirent *rd, const unsigned char *name,
+                                            uint32_t namelen, int alloc_mode)
 {
-       struct jffs2_raw_node_ref *raw;
        struct jffs2_full_dirent *fd;
        size_t retlen;
        struct kvec vecs[2];
+       uint32_t flash_ofs;
        int retried = 0;
        int ret;
 
@@ -228,40 +213,39 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
        D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
                printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
                BUG();
+          });
+
+       if (strnlen(name, namelen) != namelen) {
+               /* This should never happen, but seems to have done on at least one
+                  occasion: https://dev.laptop.org/ticket/4184 */
+               printk(KERN_CRIT "Error in jffs2_write_dirent() -- name contains zero bytes!\n");
+               printk(KERN_CRIT "Directory inode #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x\n",
+                      je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
+                      je32_to_cpu(rd->name_crc));
+               WARN_ON(1);
+               return ERR_PTR(-EIO);
        }
-          );
 
        vecs[0].iov_base = rd;
        vecs[0].iov_len = sizeof(*rd);
        vecs[1].iov_base = (unsigned char *)name;
        vecs[1].iov_len = namelen;
 
-       jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
-
-       raw = jffs2_alloc_raw_node_ref();
-
-       if (!raw)
-               return ERR_PTR(-ENOMEM);
-
        fd = jffs2_alloc_full_dirent(namelen+1);
-       if (!fd) {
-               jffs2_free_raw_node_ref(raw);
+       if (!fd)
                return ERR_PTR(-ENOMEM);
-       }
 
        fd->version = je32_to_cpu(rd->version);
        fd->ino = je32_to_cpu(rd->ino);
-       fd->nhash = full_name_hash(name, strlen(name));
+       fd->nhash = full_name_hash(name, namelen);
        fd->type = rd->type;
        memcpy(fd->name, name, namelen);
        fd->name[namelen]=0;
 
  retry:
-       fd->raw = raw;
+       flash_ofs = write_ofs(c);
 
-       raw->flash_offset = flash_ofs;
-       raw->__totlen = PAD(sizeof(*rd)+namelen);
-       raw->next_phys = NULL;
+       jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len);
 
        if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
                BUG_ON(!retried);
@@ -280,15 +264,11 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
                               sizeof(*rd)+namelen, flash_ofs, ret, retlen);
                /* Mark the space as dirtied */
                if (retlen) {
-                       raw->next_in_ino = NULL;
-                       raw->flash_offset |= REF_OBSOLETE;
-                       jffs2_add_physical_node_ref(c, raw);
-                       jffs2_mark_node_obsolete(c, raw);
+                       jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*rd)+namelen), NULL);
                } else {
-                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
-                       jffs2_free_raw_node_ref(raw);
+                       printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs);
                }
-               if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
+               if (!retried) {
                        /* Try to reallocate space and retry */
                        uint32_t dummy;
                        struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
@@ -301,39 +281,40 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
                        jffs2_dbg_acct_paranoia_check(c, jeb);
 
                        if (alloc_mode == ALLOC_GC) {
-                               ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs,
-                                                       &dummy, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+                               ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &dummy,
+                                                            JFFS2_SUMMARY_DIRENT_SIZE(namelen));
                        } else {
                                /* Locking pain */
-                               up(&f->sem);
+                               mutex_unlock(&f->sem);
                                jffs2_complete_reservation(c);
 
-                               ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs,
-                                                       &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
-                               down(&f->sem);
+                               ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &dummy,
+                                                         alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
+                               mutex_lock(&f->sem);
                        }
 
                        if (!ret) {
+                               flash_ofs = write_ofs(c);
                                D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
                                jffs2_dbg_acct_sanity_check(c,jeb);
                                jffs2_dbg_acct_paranoia_check(c, jeb);
                                goto retry;
                        }
                        D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
-                       jffs2_free_raw_node_ref(raw);
                }
                /* Release the full_dnode which is now useless, and return */
                jffs2_free_full_dirent(fd);
                return ERR_PTR(ret?ret:-EIO);
        }
        /* Mark the space used */
-       raw->flash_offset |= REF_PRISTINE;
-       jffs2_add_physical_node_ref(c, raw);
-
-       spin_lock(&c->erase_completion_lock);
-       raw->next_in_ino = f->inocache->nodes;
-       f->inocache->nodes = raw;
-       spin_unlock(&c->erase_completion_lock);
+       fd->raw = jffs2_add_physical_node_ref(c, flash_ofs | dirent_node_state(rd),
+                                             PAD(sizeof(*rd)+namelen), f->inocache);
+       if (IS_ERR(fd->raw)) {
+               void *hold_err = fd->raw;
+               /* Release the full_dirent which is now useless, and return */
+               jffs2_free_full_dirent(fd);
+               return ERR_CAST(hold_err);
+       }
 
        if (retried) {
                jffs2_dbg_acct_sanity_check(c,NULL);
@@ -359,20 +340,20 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                struct jffs2_full_dnode *fn;
                unsigned char *comprbuf = NULL;
                uint16_t comprtype = JFFS2_COMPR_NONE;
-               uint32_t phys_ofs, alloclen;
+               uint32_t alloclen;
                uint32_t datalen, cdatalen;
                int retried = 0;
 
        retry:
                D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
 
-               ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs,
+               ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN,
                                        &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
                if (ret) {
                        D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
                        break;
                }
-               down(&f->sem);
+               mutex_lock(&f->sem);
                datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
                cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
 
@@ -394,13 +375,13 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
                ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 
-               fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
+               fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, ALLOC_NORETRY);
 
                jffs2_free_comprbuf(comprbuf, buf);
 
                if (IS_ERR(fn)) {
                        ret = PTR_ERR(fn);
-                       up(&f->sem);
+                       mutex_unlock(&f->sem);
                        jffs2_complete_reservation(c);
                        if (!retried) {
                                /* Write error to be retried */
@@ -422,11 +403,11 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                        jffs2_mark_node_obsolete(c, fn->raw);
                        jffs2_free_full_dnode(fn);
 
-                       up(&f->sem);
+                       mutex_unlock(&f->sem);
                        jffs2_complete_reservation(c);
                        break;
                }
-               up(&f->sem);
+               mutex_unlock(&f->sem);
                jffs2_complete_reservation(c);
                if (!datalen) {
                        printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
@@ -448,24 +429,24 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
        struct jffs2_raw_dirent *rd;
        struct jffs2_full_dnode *fn;
        struct jffs2_full_dirent *fd;
-       uint32_t alloclen, phys_ofs;
+       uint32_t alloclen;
        int ret;
 
        /* Try to reserve enough space for both node and dirent.
         * Just the node will do for now, though
         */
-       ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL,
+       ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL,
                                JFFS2_SUMMARY_INODE_SIZE);
        D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
-       if (ret) {
-               up(&f->sem);
+       if (ret)
                return ret;
-       }
+
+       mutex_lock(&f->sem);
 
        ri->data_crc = cpu_to_je32(0);
        ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 
-       fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+       fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL);
 
        D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
                  jemode_to_cpu(ri->mode)));
@@ -473,7 +454,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
        if (IS_ERR(fn)) {
                D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
                /* Eeek. Wave bye bye */
-               up(&f->sem);
+               mutex_unlock(&f->sem);
                jffs2_complete_reservation(c);
                return PTR_ERR(fn);
        }
@@ -482,9 +463,17 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
        */
        f->metadata = fn;
 
-       up(&f->sem);
+       mutex_unlock(&f->sem);
        jffs2_complete_reservation(c);
-       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+
+       ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode);
+       if (ret)
+               return ret;
+       ret = jffs2_init_acl_post(&f->vfs_inode);
+       if (ret)
+               return ret;
+
+       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
                                ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
 
        if (ret) {
@@ -500,7 +489,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
                return -ENOMEM;
        }
 
-       down(&dir_f->sem);
+       mutex_lock(&dir_f->sem);
 
        rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
        rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
@@ -516,7 +505,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
        rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
        rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
-       fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
+       fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, ALLOC_NORMAL);
 
        jffs2_free_raw_dirent(rd);
 
@@ -524,7 +513,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
                /* dirent failed to write. Delete the inode normally
                   as if it were the final unlink() */
                jffs2_complete_reservation(c);
-               up(&dir_f->sem);
+               mutex_unlock(&dir_f->sem);
                return PTR_ERR(fd);
        }
 
@@ -533,7 +522,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str
        jffs2_add_fd_to_list(c, fd, &dir_f->dents);
 
        jffs2_complete_reservation(c);
-       up(&dir_f->sem);
+       mutex_unlock(&dir_f->sem);
 
        return 0;
 }
@@ -545,25 +534,24 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
 {
        struct jffs2_raw_dirent *rd;
        struct jffs2_full_dirent *fd;
-       uint32_t alloclen, phys_ofs;
+       uint32_t alloclen;
        int ret;
 
-       if (1 /* alternative branch needs testing */ ||
-           !jffs2_can_mark_obsolete(c)) {
+       if (!jffs2_can_mark_obsolete(c)) {
                /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
 
                rd = jffs2_alloc_raw_dirent();
                if (!rd)
                        return -ENOMEM;
 
-               ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+               ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
                                        ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
                if (ret) {
                        jffs2_free_raw_dirent(rd);
                        return ret;
                }
 
-               down(&dir_f->sem);
+               mutex_lock(&dir_f->sem);
 
                /* Build a deletion node */
                rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -580,42 +568,46 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
                rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
                rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
-               fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
+               fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, ALLOC_DELETION);
 
                jffs2_free_raw_dirent(rd);
 
                if (IS_ERR(fd)) {
                        jffs2_complete_reservation(c);
-                       up(&dir_f->sem);
+                       mutex_unlock(&dir_f->sem);
                        return PTR_ERR(fd);
                }
 
                /* File it. This will mark the old one obsolete. */
                jffs2_add_fd_to_list(c, fd, &dir_f->dents);
-               up(&dir_f->sem);
+               mutex_unlock(&dir_f->sem);
        } else {
-               struct jffs2_full_dirent **prev = &dir_f->dents;
                uint32_t nhash = full_name_hash(name, namelen);
 
-               down(&dir_f->sem);
+               fd = dir_f->dents;
+               /* We don't actually want to reserve any space, but we do
+                  want to be holding the alloc_sem when we write to flash */
+               mutex_lock(&c->alloc_sem);
+               mutex_lock(&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);
                }
-               up(&dir_f->sem);
+               mutex_unlock(&dir_f->sem);
        }
 
        /* dead_f is NULL if this was a rename not a real unlink */
@@ -623,7 +615,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
           pointing to an inode which didn't exist. */
        if (dead_f && dead_f->inocache) {
 
-               down(&dead_f->sem);
+               mutex_lock(&dead_f->sem);
 
                if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
                        while (dead_f->dents) {
@@ -639,14 +631,15 @@ 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);
                        }
-               }
-
-               dead_f->inocache->nlink--;
+                       dead_f->inocache->pino_nlink = 0;
+               } else
+                       dead_f->inocache->pino_nlink--;
                /* NB: Caller must set inode nlink if appropriate */
-               up(&dead_f->sem);
+               mutex_unlock(&dead_f->sem);
        }
 
        jffs2_complete_reservation(c);
@@ -659,21 +652,21 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint
 {
        struct jffs2_raw_dirent *rd;
        struct jffs2_full_dirent *fd;
-       uint32_t alloclen, phys_ofs;
+       uint32_t alloclen;
        int ret;
 
        rd = jffs2_alloc_raw_dirent();
        if (!rd)
                return -ENOMEM;
 
-       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
                                ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
        if (ret) {
                jffs2_free_raw_dirent(rd);
                return ret;
        }
 
-       down(&dir_f->sem);
+       mutex_lock(&dir_f->sem);
 
        /* Build a deletion node */
        rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -692,13 +685,13 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint
        rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
        rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
-       fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
+       fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, ALLOC_NORMAL);
 
        jffs2_free_raw_dirent(rd);
 
        if (IS_ERR(fd)) {
                jffs2_complete_reservation(c);
-               up(&dir_f->sem);
+               mutex_unlock(&dir_f->sem);
                return PTR_ERR(fd);
        }
 
@@ -706,7 +699,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint
        jffs2_add_fd_to_list(c, fd, &dir_f->dents);
 
        jffs2_complete_reservation(c);
-       up(&dir_f->sem);
+       mutex_unlock(&dir_f->sem);
 
        return 0;
 }