fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / jfs / namei.c
index abfe33f..fa91e48 100644 (file)
@@ -1,23 +1,26 @@
 /*
- *   Copyright (C) International Business Machines Corp., 2000-2003
+ *   Copyright (C) International Business Machines Corp., 2000-2004
  *   Portions Copyright (C) Christoph Hellwig, 2001-2002
  *
  *   This program is free software;  you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or 
+ *   the Free Software Foundation; either version 2 of the License, or
  *   (at your option) any later version.
- * 
+ *
  *   This program is distributed in the hope that it will be useful,
  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  *   the GNU General Public License for more details.
  *
  *   You should have received a copy of the GNU General Public License
- *   along with this program;  if not, write to the Free Software 
+ *   along with this program;  if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
 #include <linux/fs.h>
+#include <linux/ctype.h>
+#include <linux/quotaops.h>
+#include <linux/vs_tag.h>
 #include "jfs_incore.h"
 #include "jfs_superblock.h"
 #include "jfs_inode.h"
 #include "jfs_acl.h"
 #include "jfs_debug.h"
 
-extern struct inode_operations jfs_file_inode_operations;
-extern struct inode_operations jfs_symlink_inode_operations;
-extern struct file_operations jfs_file_operations;
-extern struct address_space_operations jfs_aops;
-
-extern int jfs_fsync(struct file *, struct dentry *, int);
-extern void jfs_truncate_nolock(struct inode *, loff_t);
-extern int jfs_init_acl(struct inode *, struct inode *);
-
 /*
  * forward references
  */
-struct inode_operations jfs_dir_inode_operations;
-struct file_operations jfs_dir_operations;
+struct dentry_operations jfs_ci_dentry_operations;
 
 static s64 commitZeroLink(tid_t, struct inode *);
 
+/*
+ * NAME:       free_ea_wmap(inode)
+ *
+ * FUNCTION:   free uncommitted extended attributes from working map
+ *
+ */
+static inline void free_ea_wmap(struct inode *inode)
+{
+       dxd_t *ea = &JFS_IP(inode)->ea;
+
+       if (ea->flag & DXD_EXTENT) {
+               /* free EA pages from cache */
+               invalidate_dxd_metapages(inode, *ea);
+               dbFree(inode, addressDXD(ea), lengthDXD(ea));
+       }
+       ea->flag = 0;
+}
+
 /*
  * NAME:       jfs_create(dip, dentry, mode)
  *
  * FUNCTION:   create a regular file in the parent directory <dip>
  *             with name = <from dentry> and mode = <mode>
  *
- * PARAMETER:  dip     - parent directory vnode
+ * PARAMETER:  dip     - parent directory vnode
  *             dentry  - dentry of new file
  *             mode    - create mode (rwxrwxrwx).
  *             nd- nd struct
@@ -87,18 +98,29 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
         * begin the transaction before we search the directory.
         */
        ip = ialloc(dip, mode);
-       if (ip == NULL) {
-               rc = -ENOSPC;
+       if (IS_ERR(ip)) {
+               rc = PTR_ERR(ip);
                goto out2;
        }
 
        tid = txBegin(dip->i_sb, 0);
 
-       down(&JFS_IP(dip)->commit_sem);
-       down(&JFS_IP(ip)->commit_sem);
+       mutex_lock(&JFS_IP(dip)->commit_mutex);
+       mutex_lock(&JFS_IP(ip)->commit_mutex);
+
+       rc = jfs_init_acl(tid, ip, dip);
+       if (rc)
+               goto out3;
+
+       rc = jfs_init_security(tid, ip, dip);
+       if (rc) {
+               txAbort(tid, 0);
+               goto out3;
+       }
 
        if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) {
                jfs_err("jfs_create: dtSearch returned %d", rc);
+               txAbort(tid, 0);
                goto out3;
        }
 
@@ -121,10 +143,10 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
         */
        ino = ip->i_ino;
        if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
-               jfs_err("jfs_create: dtInsert returned %d", rc);
-               if (rc == -EIO)
+               if (rc == -EIO) {
+                       jfs_err("jfs_create: dtInsert returned -EIO");
                        txAbort(tid, 1);        /* Marks Filesystem dirty */
-               else
+               else
                        txAbort(tid, 0);        /* Filesystem full */
                goto out3;
        }
@@ -135,7 +157,6 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
 
        insert_inode_hash(ip);
        mark_inode_dirty(ip);
-       d_instantiate(dentry, ip);
 
        dip->i_ctime = dip->i_mtime = CURRENT_TIME;
 
@@ -145,21 +166,18 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
 
       out3:
        txEnd(tid);
-       up(&JFS_IP(dip)->commit_sem);
-       up(&JFS_IP(ip)->commit_sem);
+       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(dip)->commit_mutex);
        if (rc) {
+               free_ea_wmap(ip);
                ip->i_nlink = 0;
                iput(ip);
-       }
+       } else
+               d_instantiate(dentry, ip);
 
       out2:
        free_UCSname(&dname);
 
-#ifdef CONFIG_JFS_POSIX_ACL
-       if (rc == 0)
-               jfs_init_acl(ip, dip);
-#endif
-
       out1:
 
        jfs_info("jfs_create: rc:%d", rc);
@@ -173,7 +191,7 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
  * FUNCTION:   create a child directory in the parent directory <dip>
  *             with name = <from dentry> and mode = <mode>
  *
- * PARAMETER:  dip     - parent directory vnode
+ * PARAMETER:  dip     - parent directory vnode
  *             dentry  - dentry of child directory
  *             mode    - create mode (rwxrwxrwx).
  *
@@ -214,18 +232,29 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
         * begin the transaction before we search the directory.
         */
        ip = ialloc(dip, S_IFDIR | mode);
-       if (ip == NULL) {
-               rc = -ENOSPC;
+       if (IS_ERR(ip)) {
+               rc = PTR_ERR(ip);
                goto out2;
        }
 
        tid = txBegin(dip->i_sb, 0);
 
-       down(&JFS_IP(dip)->commit_sem);
-       down(&JFS_IP(ip)->commit_sem);
+       mutex_lock(&JFS_IP(dip)->commit_mutex);
+       mutex_lock(&JFS_IP(ip)->commit_mutex);
+
+       rc = jfs_init_acl(tid, ip, dip);
+       if (rc)
+               goto out3;
+
+       rc = jfs_init_security(tid, ip, dip);
+       if (rc) {
+               txAbort(tid, 0);
+               goto out3;
+       }
 
        if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) {
                jfs_err("jfs_mkdir: dtSearch returned %d", rc);
+               txAbort(tid, 0);
                goto out3;
        }
 
@@ -248,11 +277,10 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
         */
        ino = ip->i_ino;
        if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
-               jfs_err("jfs_mkdir: dtInsert returned %d", rc);
-
-               if (rc == -EIO)
+               if (rc == -EIO) {
+                       jfs_err("jfs_mkdir: dtInsert returned -EIO");
                        txAbort(tid, 1);        /* Marks Filesystem dirty */
-               else
+               else
                        txAbort(tid, 0);        /* Filesystem full */
                goto out3;
        }
@@ -260,15 +288,12 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
        ip->i_nlink = 2;        /* for '.' */
        ip->i_op = &jfs_dir_inode_operations;
        ip->i_fop = &jfs_dir_operations;
-       ip->i_mapping->a_ops = &jfs_aops;
-       mapping_set_gfp_mask(ip->i_mapping, GFP_NOFS);
 
        insert_inode_hash(ip);
        mark_inode_dirty(ip);
-       d_instantiate(dentry, ip);
 
        /* update parent directory inode */
-       dip->i_nlink++;         /* for '..' from child directory */
+       inc_nlink(dip);         /* for '..' from child directory */
        dip->i_ctime = dip->i_mtime = CURRENT_TIME;
        mark_inode_dirty(dip);
 
@@ -276,20 +301,18 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
 
       out3:
        txEnd(tid);
-       up(&JFS_IP(dip)->commit_sem);
-       up(&JFS_IP(ip)->commit_sem);
+       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(dip)->commit_mutex);
        if (rc) {
+               free_ea_wmap(ip);
                ip->i_nlink = 0;
                iput(ip);
-       }
+       } else
+               d_instantiate(dentry, ip);
 
       out2:
        free_UCSname(&dname);
 
-#ifdef CONFIG_JFS_POSIX_ACL
-       if (rc == 0)
-               jfs_init_acl(ip, dip);
-#endif
 
       out1:
 
@@ -302,7 +325,7 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
  *
  * FUNCTION:   remove a link to child directory
  *
- * PARAMETER:  dip     - parent inode
+ * PARAMETER:  dip     - parent inode
  *             dentry  - child directory dentry
  *
  * RETURN:     -EINVAL - if name is . or ..
@@ -310,10 +333,10 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
  *             errors from subroutines
  *
  * note:
- * if other threads have the directory open when the last link 
- * is removed, the "." and ".." entries, if present, are removed before 
- * rmdir() returns and no new entries may be created in the directory, 
- * but the directory is not removed until the last reference to 
+ * if other threads have the directory open when the last link
+ * is removed, the "." and ".." entries, if present, are removed before
+ * rmdir() returns and no new entries may be created in the directory,
+ * but the directory is not removed until the last reference to
  * the directory is released (cf.unlink() of regular file).
  */
 static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
@@ -328,6 +351,9 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
 
        jfs_info("jfs_rmdir: dip:0x%p name:%s", dip, dentry->d_name.name);
 
+       /* Init inode for quota operations. */
+       DQUOT_INIT(ip);
+
        /* directory must be empty to be removed */
        if (!dtEmpty(ip)) {
                rc = -ENOTEMPTY;
@@ -340,8 +366,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
 
        tid = txBegin(dip->i_sb, 0);
 
-       down(&JFS_IP(dip)->commit_sem);
-       down(&JFS_IP(ip)->commit_sem);
+       mutex_lock(&JFS_IP(dip)->commit_mutex);
+       mutex_lock(&JFS_IP(ip)->commit_mutex);
 
        iplist[0] = dip;
        iplist[1] = ip;
@@ -359,8 +385,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
                if (rc == -EIO)
                        txAbort(tid, 1);
                txEnd(tid);
-               up(&JFS_IP(dip)->commit_sem);
-               up(&JFS_IP(ip)->commit_sem);
+               mutex_unlock(&JFS_IP(ip)->commit_mutex);
+               mutex_unlock(&JFS_IP(dip)->commit_mutex);
 
                goto out2;
        }
@@ -368,9 +394,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
        /* update parent directory's link count corresponding
         * to ".." entry of the target directory deleted
         */
-       dip->i_nlink--;
        dip->i_ctime = dip->i_mtime = CURRENT_TIME;
-       mark_inode_dirty(dip);
+       inode_dec_link_count(dip);
 
        /*
         * OS/2 could have created EA and/or ACL
@@ -390,15 +415,15 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
        JFS_IP(ip)->acl.flag = 0;
 
        /* mark the target directory as deleted */
-       ip->i_nlink = 0;
+       clear_nlink(ip);
        mark_inode_dirty(ip);
 
        rc = txCommit(tid, 2, &iplist[0], 0);
 
        txEnd(tid);
 
-       up(&JFS_IP(dip)->commit_sem);
-       up(&JFS_IP(ip)->commit_sem);
+       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(dip)->commit_mutex);
 
        /*
         * Truncating the directory index table is not guaranteed.  It
@@ -422,11 +447,11 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
 /*
  * NAME:       jfs_unlink(dip, dentry)
  *
- * FUNCTION:   remove a link to object <vp> named by <name> 
+ * FUNCTION:   remove a link to object <vp> named by <name>
  *             from parent directory <dvp>
  *
- * PARAMETER:  dip     - inode of parent directory
- *             dentry  - dentry of object to be removed
+ * PARAMETER:  dip     - inode of parent directory
+ *             dentry  - dentry of object to be removed
  *
  * RETURN:     errors from subroutines
  *
@@ -453,6 +478,9 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
 
        jfs_info("jfs_unlink: dip:0x%p name:%s", dip, dentry->d_name.name);
 
+       /* Init inode for quota operations. */
+       DQUOT_INIT(ip);
+
        if ((rc = get_UCSname(&dname, dentry)))
                goto out;
 
@@ -460,8 +488,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
 
        tid = txBegin(dip->i_sb, 0);
 
-       down(&JFS_IP(dip)->commit_sem);
-       down(&JFS_IP(ip)->commit_sem);
+       mutex_lock(&JFS_IP(dip)->commit_mutex);
+       mutex_lock(&JFS_IP(ip)->commit_mutex);
 
        iplist[0] = dip;
        iplist[1] = ip;
@@ -475,8 +503,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
                if (rc == -EIO)
                        txAbort(tid, 1);        /* Marks FS Dirty */
                txEnd(tid);
-               up(&JFS_IP(dip)->commit_sem);
-               up(&JFS_IP(ip)->commit_sem);
+               mutex_unlock(&JFS_IP(ip)->commit_mutex);
+               mutex_unlock(&JFS_IP(dip)->commit_mutex);
                IWRITE_UNLOCK(ip);
                goto out1;
        }
@@ -487,8 +515,7 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
        mark_inode_dirty(dip);
 
        /* update target's inode */
-       ip->i_nlink--;
-       mark_inode_dirty(ip);
+       inode_dec_link_count(ip);
 
        /*
         *      commit zero link count object
@@ -499,8 +526,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
                if ((new_size = commitZeroLink(tid, ip)) < 0) {
                        txAbort(tid, 1);        /* Marks FS Dirty */
                        txEnd(tid);
-                       up(&JFS_IP(dip)->commit_sem);
-                       up(&JFS_IP(ip)->commit_sem);
+                       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+                       mutex_unlock(&JFS_IP(dip)->commit_mutex);
                        IWRITE_UNLOCK(ip);
                        rc = new_size;
                        goto out1;
@@ -528,13 +555,12 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
 
        txEnd(tid);
 
-       up(&JFS_IP(dip)->commit_sem);
-       up(&JFS_IP(ip)->commit_sem);
-
+       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(dip)->commit_mutex);
 
        while (new_size && (rc == 0)) {
                tid = txBegin(dip->i_sb, 0);
-               down(&JFS_IP(ip)->commit_sem);
+               mutex_lock(&JFS_IP(ip)->commit_mutex);
                new_size = xtTruncate_pmap(tid, ip, new_size);
                if (new_size < 0) {
                        txAbort(tid, 1);        /* Marks FS Dirty */
@@ -542,7 +568,7 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
                } else
                        rc = txCommit(tid, 2, &iplist[0], COMMIT_SYNC);
                txEnd(tid);
-               up(&JFS_IP(ip)->commit_sem);
+               mutex_unlock(&JFS_IP(ip)->commit_mutex);
        }
 
        if (ip->i_nlink == 0)
@@ -573,7 +599,7 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
  *
  * FUNCTION:    for non-directory, called by jfs_remove(),
  *             truncate a regular file, directory or symbolic
- *             link to zero length. return 0 if type is not 
+ *             link to zero length. return 0 if type is not
  *             one of these.
  *
  *             if the file is currently associated with a VM segment
@@ -583,7 +609,7 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
  *             map by ctrunc1.
  *             if there is no VM segment on entry, the resources are
  *             freed in both work and permanent map.
- *             (? for temporary file - memory object is cached even 
+ *             (? for temporary file - memory object is cached even
  *             after no reference:
  *             reference count > 0 -   )
  *
@@ -637,7 +663,7 @@ static s64 commitZeroLink(tid_t tid, struct inode *ip)
 
        /*
         * free xtree/data (truncate to zero length):
-        * free xtree/data pages from cache if COMMIT_PWMAP, 
+        * free xtree/data pages from cache if COMMIT_PWMAP,
         * free xtree/data blocks from persistent block map, and
         * free xtree/data blocks from working block map if COMMIT_PWMAP;
         */
@@ -649,23 +675,20 @@ static s64 commitZeroLink(tid_t tid, struct inode *ip)
 
 
 /*
- * NAME:       freeZeroLink()
+ * NAME:       jfs_free_zero_link()
  *
  * FUNCTION:    for non-directory, called by iClose(),
- *             free resources of a file from cache and WORKING map 
+ *             free resources of a file from cache and WORKING map
  *             for a file previously committed with zero link count
  *             while associated with a pager object,
  *
  * PARAMETER:  ip      - pointer to inode of file.
- *
- * RETURN:     0 -ok
  */
-int freeZeroLink(struct inode *ip)
+void jfs_free_zero_link(struct inode *ip)
 {
-       int rc = 0;
        int type;
 
-       jfs_info("freeZeroLink: ip = 0x%p", ip);
+       jfs_info("jfs_free_zero_link: ip = 0x%p", ip);
 
        /* return if not reg or symbolic link or if size is
         * already ok.
@@ -678,10 +701,10 @@ int freeZeroLink(struct inode *ip)
        case S_IFLNK:
                /* if its contained in inode nothing to do */
                if (ip->i_size < IDATASIZE)
-                       return 0;
+                       return;
                break;
        default:
-               return 0;
+               return;
        }
 
        /*
@@ -702,7 +725,7 @@ int freeZeroLink(struct inode *ip)
                pxdlock->flag = mlckFREEPXD;
                PXDaddress(&pxdlock->pxd, xaddr);
                PXDlength(&pxdlock->pxd, xlen);
-               txFreeMap(ip, pxdlock, 0, COMMIT_WMAP);
+               txFreeMap(ip, pxdlock, NULL, COMMIT_WMAP);
        }
 
        /*
@@ -722,7 +745,7 @@ int freeZeroLink(struct inode *ip)
                pxdlock->flag = mlckFREEPXD;
                PXDaddress(&pxdlock->pxd, xaddr);
                PXDlength(&pxdlock->pxd, xlen);
-               txFreeMap(ip, pxdlock, 0, COMMIT_WMAP);
+               txFreeMap(ip, pxdlock, NULL, COMMIT_WMAP);
        }
 
        /*
@@ -731,9 +754,7 @@ int freeZeroLink(struct inode *ip)
         * free xtree/data blocks from working block map;
         */
        if (ip->i_size)
-               rc = xtTruncate(0, ip, 0, COMMIT_WMAP);
-
-       return rc;
+               xtTruncate(0, ip, 0, COMMIT_WMAP);
 }
 
 /*
@@ -742,7 +763,7 @@ int freeZeroLink(struct inode *ip)
  * FUNCTION:   create a link to <vp> by the name = <name>
  *             in the parent directory <dvp>
  *
- * PARAMETER:  vp      - target object
+ * PARAMETER:  vp      - target object
  *             dvp     - parent directory of new link
  *             name    - name of new link to target object
  *             crp     - credential
@@ -782,8 +803,8 @@ static int jfs_link(struct dentry *old_dentry,
 
        tid = txBegin(ip->i_sb, 0);
 
-       down(&JFS_IP(dir)->commit_sem);
-       down(&JFS_IP(ip)->commit_sem);
+       mutex_lock(&JFS_IP(dir)->commit_mutex);
+       mutex_lock(&JFS_IP(ip)->commit_mutex);
 
        /*
         * scan parent directory for entry/freespace
@@ -802,24 +823,30 @@ static int jfs_link(struct dentry *old_dentry,
                goto free_dname;
 
        /* update object inode */
-       ip->i_nlink++;          /* for new link */
+       inc_nlink(ip);          /* for new link */
        ip->i_ctime = CURRENT_TIME;
+       dir->i_ctime = dir->i_mtime = CURRENT_TIME;
        mark_inode_dirty(dir);
        atomic_inc(&ip->i_count);
-       d_instantiate(dentry, ip);
 
        iplist[0] = ip;
        iplist[1] = dir;
        rc = txCommit(tid, 2, &iplist[0], 0);
 
+       if (rc) {
+               ip->i_nlink--; /* never instantiated */
+               iput(ip);
+       } else
+               d_instantiate(dentry, ip);
+
       free_dname:
        free_UCSname(&dname);
 
       out:
        txEnd(tid);
 
-       up(&JFS_IP(dir)->commit_sem);
-       up(&JFS_IP(ip)->commit_sem);
+       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(dir)->commit_mutex);
 
        jfs_info("jfs_link: rc:%d", rc);
        return rc;
@@ -832,8 +859,8 @@ static int jfs_link(struct dentry *old_dentry,
  *                     in directory <dip>
  *
  * PARAMETER:  dip         - parent directory vnode
- *                     dentry  - dentry of symbolic link
- *                     name    - the path name of the existing object 
+ *                     dentry  - dentry of symbolic link
+ *                     name    - the path name of the existing object
  *                                   that will be the source of the link
  *
  * RETURN:     errors from subroutines
@@ -856,7 +883,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
        unchar *i_fastsymlink;
        s64 xlen = 0;
        int bmask = 0, xsize;
-       s64 xaddr;
+       s64 extent = 0, xaddr;
        struct metapage *mp;
        struct super_block *sb;
        struct tblock *tblk;
@@ -880,17 +907,18 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
         * (iAlloc() returns new, locked inode)
         */
        ip = ialloc(dip, S_IFLNK | 0777);
-       if (ip == NULL) {
-               rc = -ENOSPC;
+       if (IS_ERR(ip)) {
+               rc = PTR_ERR(ip);
                goto out2;
        }
 
        tid = txBegin(dip->i_sb, 0);
 
-       down(&JFS_IP(dip)->commit_sem);
-       down(&JFS_IP(ip)->commit_sem);
+       mutex_lock(&JFS_IP(dip)->commit_mutex);
+       mutex_lock(&JFS_IP(ip)->commit_mutex);
 
-       if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE)))
+       rc = jfs_init_security(tid, ip, dip);
+       if (rc)
                goto out3;
 
        tblk = tid_to_tblock(tid);
@@ -898,29 +926,14 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
        tblk->ino = ip->i_ino;
        tblk->u.ixpxd = JFS_IP(ip)->ixpxd;
 
-       /*
-        * create entry for symbolic link in parent directory
-        */
-
-       ino = ip->i_ino;
-
-
-
-       if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
-               jfs_err("jfs_symlink: dtInsert returned %d", rc);
-               /* discard ne inode */
-               goto out3;
-
-       }
-
        /* fix symlink access permission
-        * (dir_create() ANDs in the u.u_cmask, 
+        * (dir_create() ANDs in the u.u_cmask,
         * but symlinks really need to be 777 access)
         */
        ip->i_mode |= 0777;
 
        /*
-          *       write symbolic link target path name
+        * write symbolic link target path name
         */
        xtInitRoot(tid, ip);
 
@@ -955,7 +968,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
                ip->i_mapping->a_ops = &jfs_aops;
 
                /*
-                * even though the data of symlink object (source 
+                * even though the data of symlink object (source
                 * path name) is treated as non-journaled user data,
                 * it is read/written thru buffer cache for performance.
                 */
@@ -964,80 +977,75 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
                xsize = (ssize + bmask) & ~bmask;
                xaddr = 0;
                xlen = xsize >> JFS_SBI(sb)->l2bsize;
-               if ((rc = xtInsert(tid, ip, 0, 0, xlen, &xaddr, 0)) == 0) {
-                       ip->i_size = ssize - 1;
-                       while (ssize) {
-                               int copy_size = min(ssize, PSIZE);
-
-                               mp = get_metapage(ip, xaddr, PSIZE, 1);
-
-                               if (mp == NULL) {
-                                       dtDelete(tid, dip, &dname, &ino,
-                                                JFS_REMOVE);
-                                       rc = -EIO;
-                                       goto out3;
-                               }
-                               memcpy(mp->data, name, copy_size);
-                               flush_metapage(mp);
-#if 0
-                               set_buffer_uptodate(bp);
-                               mark_buffer_dirty(bp, 1);
-                               if (IS_SYNC(dip))
-                                       sync_dirty_buffer(bp);
-                               brelse(bp);
-#endif                         /* 0 */
-                               ssize -= copy_size;
-                               xaddr += JFS_SBI(sb)->nbperpage;
-                       }
-                       ip->i_blocks = LBLK2PBLK(sb, xlen);
-               } else {
-                       dtDelete(tid, dip, &dname, &ino, JFS_REMOVE);
-                       rc = -ENOSPC;
+               if ((rc = xtInsert(tid, ip, 0, 0, xlen, &xaddr, 0))) {
+                       txAbort(tid, 0);
                        goto out3;
                }
+               extent = xaddr;
+               ip->i_size = ssize - 1;
+               while (ssize) {
+                       /* This is kind of silly since PATH_MAX == 4K */
+                       int copy_size = min(ssize, PSIZE);
+
+                       mp = get_metapage(ip, xaddr, PSIZE, 1);
+
+                       if (mp == NULL) {
+                               xtTruncate(tid, ip, 0, COMMIT_PWMAP);
+                               rc = -EIO;
+                               txAbort(tid, 0);
+                               goto out3;
+                       }
+                       memcpy(mp->data, name, copy_size);
+                       flush_metapage(mp);
+                       ssize -= copy_size;
+                       name += copy_size;
+                       xaddr += JFS_SBI(sb)->nbperpage;
+               }
+       }
+
+       /*
+        * create entry for symbolic link in parent directory
+        */
+       rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE);
+       if (rc == 0) {
+               ino = ip->i_ino;
+               rc = dtInsert(tid, dip, &dname, &ino, &btstack);
+       }
+       if (rc) {
+               if (xlen)
+                       xtTruncate(tid, ip, 0, COMMIT_PWMAP);
+               txAbort(tid, 0);
+               /* discard new inode */
+               goto out3;
        }
 
        insert_inode_hash(ip);
        mark_inode_dirty(ip);
-       d_instantiate(dentry, ip);
 
+       dip->i_ctime = dip->i_mtime = CURRENT_TIME;
+       mark_inode_dirty(dip);
        /*
         * commit update of parent directory and link object
-        *
-        * if extent allocation failed (ENOSPC),
-        * the parent inode is committed regardless to avoid
-        * backing out parent directory update (by dtInsert())
-        * and subsequent dtDelete() which is harmless wrt 
-        * integrity concern.  
-        * the symlink inode will be freed by iput() at exit
-        * as it has a zero link count (by dtDelete()) and 
-        * no permanant resources. 
         */
 
        iplist[0] = dip;
-       if (rc == 0) {
-               iplist[1] = ip;
-               rc = txCommit(tid, 2, &iplist[0], 0);
-       } else
-               rc = txCommit(tid, 1, &iplist[0], 0);
+       iplist[1] = ip;
+       rc = txCommit(tid, 2, &iplist[0], 0);
 
       out3:
        txEnd(tid);
-       up(&JFS_IP(dip)->commit_sem);
-       up(&JFS_IP(ip)->commit_sem);
+       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(dip)->commit_mutex);
        if (rc) {
+               free_ea_wmap(ip);
                ip->i_nlink = 0;
                iput(ip);
-       }
+       } else
+               d_instantiate(dentry, ip);
 
       out2:
        free_UCSname(&dname);
 
-#ifdef CONFIG_JFS_POSIX_ACL
-       if (rc == 0)
-               jfs_init_acl(ip, dip);
-#endif
-
       out1:
        jfs_info("jfs_symlink: rc:%d", rc);
        return rc;
@@ -1119,21 +1127,24 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        rc = -EMLINK;
                        goto out3;
                }
-       } else if (new_ip)
+       } else if (new_ip) {
                IWRITE_LOCK(new_ip);
+               /* Init inode for quota operations. */
+               DQUOT_INIT(new_ip);
+       }
 
        /*
         * The real work starts here
         */
        tid = txBegin(new_dir->i_sb, 0);
 
-       down(&JFS_IP(new_dir)->commit_sem);
-       down(&JFS_IP(old_ip)->commit_sem);
+       mutex_lock(&JFS_IP(new_dir)->commit_mutex);
+       mutex_lock(&JFS_IP(old_ip)->commit_mutex);
        if (old_dir != new_dir)
-               down(&JFS_IP(old_dir)->commit_sem);
+               mutex_lock(&JFS_IP(old_dir)->commit_mutex);
 
        if (new_ip) {
-               down(&JFS_IP(new_ip)->commit_sem);
+               mutex_lock(&JFS_IP(new_ip)->commit_mutex);
                /*
                 * Change existing directory entry to new inode number
                 */
@@ -1142,14 +1153,15 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                              old_ip->i_ino, JFS_RENAME);
                if (rc)
                        goto out4;
-               new_ip->i_nlink--;
+               drop_nlink(new_ip);
                if (S_ISDIR(new_ip->i_mode)) {
-                       new_ip->i_nlink--;
+                       drop_nlink(new_ip);
                        if (new_ip->i_nlink) {
-                               up(&JFS_IP(new_dir)->commit_sem);
-                               up(&JFS_IP(old_ip)->commit_sem);
+                               mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
                                if (old_dir != new_dir)
-                                       up(&JFS_IP(old_dir)->commit_sem);
+                                       mutex_unlock(&JFS_IP(old_dir)->commit_mutex);
+                               mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
+                               mutex_unlock(&JFS_IP(new_dir)->commit_mutex);
                                if (!S_ISDIR(old_ip->i_mode) && new_ip)
                                        IWRITE_UNLOCK(new_ip);
                                jfs_error(new_ip->i_sb,
@@ -1164,7 +1176,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        /* free block resources */
                        if ((new_size = commitZeroLink(tid, new_ip)) < 0) {
                                txAbort(tid, 1);        /* Marks FS Dirty */
-                               rc = new_size;          
+                               rc = new_size;
                                goto out4;
                        }
                        tblk = tid_to_tblock(tid);
@@ -1189,12 +1201,12 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                ino = old_ip->i_ino;
                rc = dtInsert(tid, new_dir, &new_dname, &ino, &btstack);
                if (rc) {
-                       jfs_err("jfs_rename: dtInsert failed w/rc = %d",
-                               rc);
+                       if (rc == -EIO)
+                               jfs_err("jfs_rename: dtInsert returned -EIO");
                        goto out4;
                }
                if (S_ISDIR(old_ip->i_mode))
-                       new_dir->i_nlink++;
+                       inc_nlink(new_dir);
        }
        /*
         * Remove old directory entry
@@ -1209,7 +1221,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto out4;
        }
        if (S_ISDIR(old_ip->i_mode)) {
-               old_dir->i_nlink--;
+               drop_nlink(old_dir);
                if (old_dir != new_dir) {
                        /*
                         * Change inode number of parent for moved directory
@@ -1221,7 +1233,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        /* Linelock header of dtree */
                        tlck = txLock(tid, old_ip,
                                    (struct metapage *) &JFS_IP(old_ip)->bxflag,
-                                     tlckDTREE | tlckBTROOT);
+                                     tlckDTREE | tlckBTROOT | tlckRELINK);
                        dtlck = (struct dt_lock *) & tlck->lock;
                        ASSERT(dtlck->index == 0);
                        lv = & dtlck->lv[0];
@@ -1237,7 +1249,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        old_ip->i_ctime = CURRENT_TIME;
        mark_inode_dirty(old_ip);
 
-       new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
+       new_dir->i_ctime = new_dir->i_mtime = current_fs_time(new_dir->i_sb);
        mark_inode_dirty(new_dir);
 
        /* Build list of inodes modified by this transaction */
@@ -1267,25 +1279,24 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 
       out4:
        txEnd(tid);
-
-       up(&JFS_IP(new_dir)->commit_sem);
-       up(&JFS_IP(old_ip)->commit_sem);
-       if (old_dir != new_dir)
-               up(&JFS_IP(old_dir)->commit_sem);
        if (new_ip)
-               up(&JFS_IP(new_ip)->commit_sem);
+               mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
+       if (old_dir != new_dir)
+               mutex_unlock(&JFS_IP(old_dir)->commit_mutex);
+       mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(new_dir)->commit_mutex);
 
        while (new_size && (rc == 0)) {
                tid = txBegin(new_ip->i_sb, 0);
-               down(&JFS_IP(new_ip)->commit_sem);
+               mutex_lock(&JFS_IP(new_ip)->commit_mutex);
                new_size = xtTruncate_pmap(tid, new_ip, new_size);
                if (new_size < 0) {
                        txAbort(tid, 1);
-                       rc = new_size;          
+                       rc = new_size;
                } else
                        rc = txCommit(tid, 1, &new_ip, COMMIT_SYNC);
                txEnd(tid);
-               up(&JFS_IP(new_ip)->commit_sem);
+               mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
        }
        if (new_ip && (new_ip->i_nlink == 0))
                set_cflag(COMMIT_Nolink, new_ip);
@@ -1339,28 +1350,42 @@ static int jfs_mknod(struct inode *dir, struct dentry *dentry,
                goto out;
 
        ip = ialloc(dir, mode);
-       if (ip == NULL) {
-               rc = -ENOSPC;
+       if (IS_ERR(ip)) {
+               rc = PTR_ERR(ip);
                goto out1;
        }
        jfs_ip = JFS_IP(ip);
 
        tid = txBegin(dir->i_sb, 0);
 
-       down(&JFS_IP(dir)->commit_sem);
-       down(&JFS_IP(ip)->commit_sem);
+       mutex_lock(&JFS_IP(dir)->commit_mutex);
+       mutex_lock(&JFS_IP(ip)->commit_mutex);
 
-       if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
+       rc = jfs_init_acl(tid, ip, dir);
+       if (rc)
                goto out3;
 
+       rc = jfs_init_security(tid, ip, dir);
+       if (rc) {
+               txAbort(tid, 0);
+               goto out3;
+       }
+
+       if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE))) {
+               txAbort(tid, 0);
+               goto out3;
+       }
+
        tblk = tid_to_tblock(tid);
        tblk->xflag |= COMMIT_CREATE;
        tblk->ino = ip->i_ino;
        tblk->u.ixpxd = JFS_IP(ip)->ixpxd;
 
        ino = ip->i_ino;
-       if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
+       if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack))) {
+               txAbort(tid, 0);
                goto out3;
+       }
 
        ip->i_op = &jfs_file_inode_operations;
        jfs_ip->dev = new_encode_dev(rdev);
@@ -1368,7 +1393,6 @@ static int jfs_mknod(struct inode *dir, struct dentry *dentry,
 
        insert_inode_hash(ip);
        mark_inode_dirty(ip);
-       d_instantiate(dentry, ip);
 
        dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 
@@ -1380,21 +1404,18 @@ static int jfs_mknod(struct inode *dir, struct dentry *dentry,
 
       out3:
        txEnd(tid);
-       up(&JFS_IP(ip)->commit_sem);
-       up(&JFS_IP(dir)->commit_sem);
+       mutex_unlock(&JFS_IP(ip)->commit_mutex);
+       mutex_unlock(&JFS_IP(dir)->commit_mutex);
        if (rc) {
+               free_ea_wmap(ip);
                ip->i_nlink = 0;
                iput(ip);
-       }
+       } else
+               d_instantiate(dentry, ip);
 
       out1:
        free_UCSname(&dname);
 
-#ifdef CONFIG_JFS_POSIX_ACL
-       if (rc == 0)
-               jfs_init_acl(ip, dir);
-#endif
-
       out:
        jfs_info("jfs_mknod: returning %d", rc);
        return rc;
@@ -1412,6 +1433,8 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc
 
        jfs_info("jfs_lookup: name = %s", name);
 
+       if (JFS_SBI(dip->i_sb)->mntflag & JFS_OS2)
+               dentry->d_op = &jfs_ci_dentry_operations;
 
        if ((name[0] == '.') && (len == 1))
                inum = dip->i_ino;
@@ -1439,7 +1462,13 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc
                return ERR_PTR(-EACCES);
        }
 
-       return d_splice_alias(ip, dentry);
+       dx_propagate_tag(nd, ip);
+       dentry = d_splice_alias(ip, dentry);
+
+       if (dentry && (JFS_SBI(dip->i_sb)->mntflag & JFS_OS2))
+               dentry->d_op = &jfs_ci_dentry_operations;
+
+       return dentry;
 }
 
 struct dentry *jfs_get_parent(struct dentry *dentry)
@@ -1486,10 +1515,55 @@ struct inode_operations jfs_dir_inode_operations = {
        .setattr        = jfs_setattr,
        .permission     = jfs_permission,
 #endif
+       .sync_flags     = jfs_sync_flags,
 };
 
-struct file_operations jfs_dir_operations = {
+const struct file_operations jfs_dir_operations = {
        .read           = generic_read_dir,
        .readdir        = jfs_readdir,
        .fsync          = jfs_fsync,
+       .ioctl          = jfs_ioctl,
+};
+
+static int jfs_ci_hash(struct dentry *dir, struct qstr *this)
+{
+       unsigned long hash;
+       int i;
+
+       hash = init_name_hash();
+       for (i=0; i < this->len; i++)
+               hash = partial_name_hash(tolower(this->name[i]), hash);
+       this->hash = end_name_hash(hash);
+
+       return 0;
+}
+
+static int jfs_ci_compare(struct dentry *dir, struct qstr *a, struct qstr *b)
+{
+       int i, result = 1;
+
+       if (a->len != b->len)
+               goto out;
+       for (i=0; i < a->len; i++) {
+               if (tolower(a->name[i]) != tolower(b->name[i]))
+                       goto out;
+       }
+       result = 0;
+
+       /*
+        * We want creates to preserve case.  A negative dentry, a, that
+        * has a different case than b may cause a new entry to be created
+        * with the wrong case.  Since we can't tell if a comes from a negative
+        * dentry, we blindly replace it with b.  This should be harmless if
+        * a is not a negative dentry.
+        */
+       memcpy((unsigned char *)a->name, b->name, a->len);
+out:
+       return result;
+}
+
+struct dentry_operations jfs_ci_dentry_operations =
+{
+       .d_hash = jfs_ci_hash,
+       .d_compare = jfs_ci_compare,
 };