Whamcloud - gitweb
Pass inline data from filesystem through obdo to VFS.
[fs/lustre-release.git] / lustre / obdfs / namei.c
index 9738874..6b80666 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  linux/fs/ext2/namei.c
+ *  linux/fs/obdfs/namei.c
  *
  * Copyright (C) 1992, 1993, 1994, 1995
  * Remy Card (card@masi.ibp.fr)
@@ -8,7 +8,7 @@
  *
  *  from
  *
- *  linux/fs/minix/namei.c
+ *  linux/fs/ext2/namei.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *
@@ -36,7 +36,7 @@
 #include <linux/iobuf.h>
 
 #include <linux/obd_support.h>
-#include "obdfs.h"
+#include <linux/obdfs.h>
 
 /*
  * define how far ahead to read directories while searching them.
@@ -70,13 +70,15 @@ static inline int ext2_match (int len, const char * const name,
  * itself (as a parameter - res_dir). It does NOT read the inode of the
  * entry - you'll have to do that yourself if you want to.
  */
-struct page * obdfs_find_entry (struct inode * dir,
+static struct page * obdfs_find_entry (struct inode * dir,
                                             const char * const name, int namelen,
-                                            struct ext2_dir_entry_2 ** res_dir)
+                                            struct ext2_dir_entry_2 ** res_dir, int lock)
 {
        struct super_block * sb;
        unsigned long offset;
        struct page * page;
+       ENTRY;
+       CDEBUG(D_INODE, "find entry for %*s\n", namelen, name);
 
        *res_dir = NULL;
        sb = dir->i_sb;
@@ -84,13 +86,20 @@ struct page * obdfs_find_entry (struct inode * dir,
        if (namelen > EXT2_NAME_LEN)
                return NULL;
 
-       for (page = 0, offset = 0; offset < dir->i_size; page++) {
+       CDEBUG(D_INODE, "dirsize is %Ld\n", dir->i_size);
+
+       page = 0;
+       offset = 0;
+       while ( offset < dir->i_size ) {
                struct ext2_dir_entry_2 * de;
                char * dlimit;
 
-               page = obdfs_getpage(dir, offset, 0, NOLOCK);
-               if ( !page ) 
+               page = obdfs_getpage(dir, offset, 0, lock);
+
+               if ( !page ) {
+                       CDEBUG(D_INODE, "No page, offset %lx\n", offset);
                        return NULL;
+               }
 
                de = (struct ext2_dir_entry_2 *) page_address(page);
                dlimit = (char *)page_address(page) + PAGE_SIZE; 
@@ -98,32 +107,47 @@ struct page * obdfs_find_entry (struct inode * dir,
                        /* this code is executed quadratically often */
                        /* do minimal checking `by hand' */
                        int de_len;
+                       /* CDEBUG(D_INODE, "Entry %p len %d, page at %#lx - %#lx , offset %lx\n",
+                              de, le16_to_cpu(de->rec_len), page_address(page),
+                              page_address(page) + PAGE_SIZE, offset); */
 
                        if ((char *) de + namelen <= dlimit &&
                            ext2_match (namelen, name, de)) {
                                /* found a match -
                                   just to be sure, do a full check */
-#if 0
-                               if (!ext2_check_dir_entry("ext2_find_entry",
-                                                         dir, de, bh, offset))
+                               if (!obdfs_check_dir_entry("ext2_find_entry",
+                                                         dir, de, page, offset))
                                        goto failure;
-#endif
                                *res_dir = de;
+                               EXIT;
                                return page;
                        }
                        /* prevent looping on a bad block */
                        de_len = le16_to_cpu(de->rec_len);
-                       if (de_len <= 0)
+                       if (de_len <= 0) {
+                               printk("Bad entry at %p len %d\n", de, de_len);
                                goto failure;
+                       }
                        offset += de_len;
                        de = (struct ext2_dir_entry_2 *)
                                ((char *) de + de_len);
+                       /* CDEBUG(D_INODE, "Next while %lx\n", offset); */
                }
+               if ( lock ) 
+                       UnlockPage(page);
                page_cache_release(page);
+               page = NULL;
+               CDEBUG(D_INODE, "Next for %lx\n", offset);
        }
 
 failure:
-       page_cache_release(page);
+       CDEBUG(D_INODE, "Negative case, page %p, offset %ld\n", page, offset);
+       if (page) {
+               if (lock) 
+                       UnlockPage(page);
+               page_cache_release(page);
+       }
+       EXIT;
        return NULL;
 }
 
@@ -132,21 +156,33 @@ struct dentry *obdfs_lookup(struct inode * dir, struct dentry *dentry)
        struct inode * inode;
        struct ext2_dir_entry_2 * de;
        struct page * page;
+       ENTRY;
 
        if (dentry->d_name.len > EXT2_NAME_LEN)
                return ERR_PTR(-ENAMETOOLONG);
 
-       page = obdfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
+       page = obdfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, LOCKED);
        inode = NULL;
+       if ( !page ) 
+               CDEBUG(D_INODE, "No page - negative entry.\n");
+       if ( page && !de ) {
+               CDEBUG(D_INODE, "Danger: PAGE but de.\n");
+               return ERR_PTR(-ENOENT);
+       }
        if (page) {
                unsigned long ino = le32_to_cpu(de->inode);
+               UnlockPage(page);
                page_cache_release(page);
                inode = iget(dir->i_sb, ino);
 
-               if (!inode)
+               if (!inode) { 
+                       CDEBUG(D_INODE, "No inode.\n");
+                       EXIT;
                        return ERR_PTR(-EACCES);
+               }
        }
        d_add(dentry, inode);
+       EXIT;
        return NULL;
 }
 
@@ -161,8 +197,11 @@ struct dentry *obdfs_lookup(struct inode * dir, struct dentry *dentry)
  * may not sleep between calling this and putting something into
  * the entry, as someone else might have used it while you slept.
 
- * returns a locked page
+ * returns a locked and held page upon success 
  */
+
+/* XXX I believe these pages should in fact NOT be locked */
+
 static struct page *obdfs_add_entry (struct inode * dir,
                                     const char * name, int namelen,
                                     struct ext2_dir_entry_2 ** res_dir,
@@ -174,47 +213,61 @@ static struct page *obdfs_add_entry (struct inode * dir,
        struct ext2_dir_entry_2 * de, * de1;
        struct super_block * sb;
 
+       ENTRY;
        *err = -EINVAL;
        *res_dir = NULL;
-       if (!dir || !dir->i_nlink)
+       if (!dir || !dir->i_nlink) {
+               EXIT;
                return NULL;
+       }
        sb = dir->i_sb;
 
-       if (!namelen)
+       if (!namelen) { 
+               EXIT;
                return NULL;
+       }
        /*
         * Is this a busy deleted directory?  Can't create new files if so
         */
        if (dir->i_size == 0)
        {
+               EXIT;
                *err = -ENOENT;
                return NULL;
        }
        page = obdfs_getpage(dir, 0, 0, LOCKED);
-       if (!page)
+       if (!page) {
+               EXIT;
                return NULL;
+       }
        rec_len = EXT2_DIR_REC_LEN(namelen);
+       CDEBUG(D_INODE, "reclen: %d\n", rec_len);
        offset = 0;
        de = (struct ext2_dir_entry_2 *) page_address(page);
        *err = -ENOSPC;
        while (1) {
+               CDEBUG(D_INODE, "Considering entry at %p, (page at %#lx - %#lx), offset %ld\n",
+                      de, page_address(page), page_address(page) + PAGE_SIZE, offset);
                if ((char *)de >= PAGE_SIZE + (char *)page_address(page)) {
                        UnlockPage(page);
                        page_cache_release(page);
                        page = obdfs_getpage(dir, offset, 1, LOCKED);
-                       if (page)
+                       if (!page) {
+                               EXIT;
                                return NULL;
+                       }
                        if (dir->i_size <= offset) {
                                if (dir->i_size == 0) {
                                        *err = -ENOENT;
+                                       EXIT;
                                        return NULL;
                                }
 
-                               ext2_debug ("creating next block\n");
+                               CDEBUG(D_INODE, "creating next block\n");
 
                                de = (struct ext2_dir_entry_2 *) page_address(page);
                                de->inode = 0;
-                               de->rec_len = le16_to_cpu(sb->s_blocksize);
+                               de->rec_len = le16_to_cpu(PAGE_SIZE);
                                dir->i_size = offset + PAGE_SIZE;
                                dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
                                mark_inode_dirty(dir);
@@ -225,35 +278,44 @@ static struct page *obdfs_add_entry (struct inode * dir,
                                de = (struct ext2_dir_entry_2 *) page_address(page);
                        }
                }
-#if 0
-               if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh,
+               CDEBUG(D_INODE, "\n");
+               if (!obdfs_check_dir_entry ("ext2_add_entry", dir, de, page,
                                           offset)) {
                        *err = -ENOENT;
-                       brelse (bh);
+                       UnlockPage(page);
+                       page_cache_release(page);
+                       EXIT;
                        return NULL;
                }
-#endif
+               CDEBUG(D_INODE, "\n");
                if (ext2_match (namelen, name, de)) {
                                *err = -EEXIST;
                                UnlockPage(page);
                                page_cache_release(page);
+                               EXIT;
                                return NULL;
                }
-               if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) ||
-                   (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
+               CDEBUG(D_INODE, "Testing for enough space at de %p\n", de);
+               if ( (le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) ||
+                    (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
                        offset += le16_to_cpu(de->rec_len);
+                       CDEBUG(D_INODE, "Found enough space de %p, offset %#lx\n", de, offset);
                        if (le32_to_cpu(de->inode)) {
+                               CDEBUG(D_INODE, "Inserting new in %p\n", de);
                                de1 = (struct ext2_dir_entry_2 *) ((char *) de +
                                        EXT2_DIR_REC_LEN(de->name_len));
+                               CDEBUG(D_INODE, "-- de1 at %p\n", de1);
                                de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) -
                                        EXT2_DIR_REC_LEN(de->name_len));
                                de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
                                de = de1;
                        }
+                       CDEBUG(D_INODE, "Reclen adjusted; copy %d bytes to %p, page at %#lx EOP at %#lx\n", namelen, de->name, page_address(page), page_address(page) + PAGE_SIZE);
                        de->inode = 0;
                        de->name_len = namelen;
                        de->file_type = 0;
                        memcpy (de->name, name, namelen);
+                       CDEBUG(D_INODE, "Copy done\n");
                        /*
                         * XXX shouldn't update any times until successful
                         * completion of syscall, but too many callers depend
@@ -272,11 +334,16 @@ static struct page *obdfs_add_entry (struct inode * dir,
                        *res_dir = de;
                        *err = 0;
                        PDEBUG(page, "addentry");
+                       CDEBUG(D_INODE, "Regular exit from add_entry");
+                       EXIT;
                        return page;
                }
+               CDEBUG(D_INODE, "\n");
                offset += le16_to_cpu(de->rec_len);
                de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
+               
        }
+       CDEBUG(D_INODE, "\n");
 
        UnlockPage(page);
        page_cache_release(page);
@@ -299,11 +366,9 @@ static int obdfs_delete_entry (struct ext2_dir_entry_2 * dir,
        pde = NULL;
        de = (struct ext2_dir_entry_2 *) page_address(page);
        while (i < PAGE_SIZE) {
-#if 0
-               if (!ext2_check_dir_entry ("ext2_delete_entry", NULL, 
-                                          de, bh, i))
+               if (!obdfs_check_dir_entry ("ext2_delete_entry", NULL, 
+                                          de, page, i))
                        return -EIO;
-#endif
                if (de == dir)  {
                        if (pde)
                                pde->rec_len =
@@ -340,6 +405,85 @@ static inline void ext2_set_de_type(struct super_block *sb,
                de->file_type = EXT2_FT_DIR;
 }
 
+
+/*
+ * Display all dentries holding the specified inode.
+ */
+#if 0
+static void show_dentry(struct list_head * dlist, int subdirs)
+{
+       struct list_head *tmp = dlist;
+
+       while ((tmp = tmp->next) != dlist) {
+               struct dentry * dentry;
+               const char * unhashed = "";
+
+               if ( subdirs ) 
+                       dentry  = list_entry(tmp, struct dentry, d_child);
+               else 
+                       dentry  = list_entry(tmp, struct dentry, d_alias);
+
+               if (list_empty(&dentry->d_hash))
+                       unhashed = "(unhashed)";
+
+               if ( dentry->d_inode ) 
+                       printk("show_dentry: %s/%s, d_count=%d%s (ino %ld, dev %d, ct %d)\n",
+                              dentry->d_parent->d_name.name,
+                              dentry->d_name.name, dentry->d_count,
+                              unhashed, dentry->d_inode->i_ino, 
+                              dentry->d_inode->i_dev, 
+                              dentry->d_inode->i_count);
+               else 
+                       printk("show_dentry: %s/%s, d_count=%d%s \n",
+                              dentry->d_parent->d_name.name,
+                              dentry->d_name.name, dentry->d_count,
+                              unhashed);
+       }
+}
+#endif
+
+
+struct inode *obdfs_new_inode(struct inode *dir)
+{
+       struct obdo *obdo;
+       struct inode *inode;
+       struct obdfs_inode_info *oinfo;
+       int err;
+
+       obdo = obdo_alloc();
+       if (!obdo) {
+               EXIT;
+               return ERR_PTR(-ENOMEM);
+       }
+
+       err = IOPS(dir, create)(IID(dir), obdo);
+       if ( err ) 
+               return ERR_PTR(err);
+
+       inode = iget(dir->i_sb, (unsigned long)obdo->o_id);
+       if (!inode) {
+               obdo_free(obdo);
+               EXIT;
+               return ERR_PTR(-EIO);
+       }
+
+       if (!list_empty(&inode->i_dentry)) {
+               CDEBUG(D_INODE, "New inode (%ld) has aliases!\n", 
+                      inode->i_ino);
+               iput(inode);
+               EXIT;
+               return ERR_PTR(-EIO);
+       }
+
+       obdo_free(obdo);
+
+       oinfo = inode->u.generic_ip;
+       INIT_LIST_HEAD(&oinfo->oi_list);
+       EXIT;
+       return inode;
+}
+
+
 /*
  * By the time this is called, we already have created
  * the directory cache entry for the new file, but it
@@ -354,23 +498,16 @@ int obdfs_create (struct inode * dir, struct dentry * dentry, int mode)
        struct page *page;
        struct ext2_dir_entry_2 * de;
        int err = -EIO;
-       struct obdfs_sb_info *sbi;
-       ino_t ino;
 
         ENTRY;
 
-       sbi = dir->i_sb->u.generic_sbp;
-       /*
-        * N.B. Several error exits in ext2_new_inode don't set err.
-        */
-       ino = sbi->osi_ops->o_create(sbi->osi_obd, 0, &err);
-       if ( ino == -1 ) 
-               return -1;
-       inode =  iget(dir->i_sb, ino);
-       if (!inode)
-               return err;
+       inode = obdfs_new_inode(dir);
+       if ( IS_ERR(inode) ) {
+               EXIT;
+               return PTR_ERR(inode);
+       }
 
-       inode->i_op = &obdfs_inode_ops;
+       inode->i_op = &obdfs_file_inode_operations;
        inode->i_mode = mode;
        mark_inode_dirty(inode);
        page = obdfs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
@@ -378,23 +515,20 @@ int obdfs_create (struct inode * dir, struct dentry * dentry, int mode)
                inode->i_nlink--;
                mark_inode_dirty(inode);
                iput (inode);
+               EXIT;
                return err;
        }
        de->inode = cpu_to_le32(inode->i_ino);
        ext2_set_de_type(dir->i_sb, de, S_IFREG);
        dir->i_version = ++event;
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           dir->i_ino, page, 0);
+
+       err = obdfs_do_writepage(dir, page, IS_SYNC(dir));
        UnlockPage(page);
-#if 0
-       if (IS_SYNC(dir)) {
-               ll_rw_block (WRITE, 1, &bh);
-               wait_on_buffer (bh);
-       }
-#endif
+
        page_cache_release(page);
        d_instantiate(dentry, inode);
-       return 0;
+       EXIT;
+       return err;
 }
 
 int obdfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
@@ -402,23 +536,15 @@ int obdfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
        struct inode * inode;
        struct page *page;
        struct ext2_dir_entry_2 * de;
-       int err = -EIO;
-       struct obdfs_sb_info *sbi;
-
-       ino_t ino;
+       int err;
 
         ENTRY;
 
-       sbi = dir->i_sb->u.generic_sbp;
-       /*
-        * N.B. Several error exits in ext2_new_inode don't set err.
-        */
-       ino = sbi->osi_ops->o_create(sbi->osi_obd, 0, &err);
-       if ( ino == -1 ) 
-               return -1;
-       inode =  iget(dir->i_sb, ino);
-       if (!inode)
-               return err;
+       inode = obdfs_new_inode(dir);
+       if ( IS_ERR(inode) ) {
+               EXIT;
+               return PTR_ERR(inode);
+       }
 
        inode->i_uid = current->fsuid;
        init_special_inode(inode, mode, rdev);
@@ -429,16 +555,10 @@ int obdfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
        dir->i_version = ++event;
        ext2_set_de_type(dir->i_sb, de, inode->i_mode);
        mark_inode_dirty(inode);
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           dir->i_ino, page, 0);
+
+       err = obdfs_do_writepage(dir, page, IS_SYNC(dir));
        UnlockPage(page);
 
-#if 0
-       if (IS_SYNC(dir)) {
-               ll_rw_block (WRITE, 1, &bh);
-               wait_on_buffer (bh);
-       }
-#endif
        d_instantiate(dentry, inode);
        page_cache_release(page);
        err = 0;
@@ -454,44 +574,36 @@ out_no_entry:
 
 int obdfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 {
-       struct inode * child;
-       struct page *page, *child_page;
+       struct inode * inode;
+       struct page *page, *inode_page;
        struct ext2_dir_entry_2 * de;
-       struct obdfs_sb_info *sbi;
        int err;
-       ino_t ino;
 
        ENTRY;
-       sbi = dir->i_sb->u.generic_sbp;
 
        err = -EMLINK;
        if (dir->i_nlink >= EXT2_LINK_MAX)
                goto out;
 
-       err = -EIO;
-       ino = iops(dir)->o_create(sbi->osi_obd, 0, &err);
-       if ( ino == -1 ) 
-               return -1;
-       child =  iget(dir->i_sb, ino);
-       if (!child)
-               return err;
-
+       inode = obdfs_new_inode(dir);
+       if ( IS_ERR(inode) ) {
+               EXIT;
+               return PTR_ERR(inode);
+       }
 
-       child->i_op = &obdfs_inode_ops;
-       child->i_size = PAGE_SIZE;
-       child->i_blocks = 0;    
-       child_page = obdfs_getpage(child, 0, 1, LOCKED);
-       if (!child_page) {
-               child->i_nlink--; /* is this nlink == 0? */
-               mark_inode_dirty(child);
-               iput (child);
+       inode->i_op = &obdfs_dir_inode_operations;
+       inode->i_blocks = 0;    
+       inode_page = obdfs_getpage(inode, 0, 1, LOCKED);
+       if (!inode_page) {
+               inode->i_nlink--; /* is this nlink == 0? */
+               mark_inode_dirty(inode);
+               iput (inode);
                return err;
        }
-       child->i_blocks = PAGE_SIZE/child->i_sb->s_blocksize;
 
        /* create . and .. */
-       de = (struct ext2_dir_entry_2 *) page_address(child_page);
-       de->inode = cpu_to_le32(child->i_ino);
+       de = (struct ext2_dir_entry_2 *) page_address(inode_page);
+       de->inode = cpu_to_le32(inode->i_ino);
        de->name_len = 1;
        de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
        strcpy (de->name, ".");
@@ -499,20 +611,23 @@ int obdfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        
        de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
        de->inode = cpu_to_le32(dir->i_ino);
-       de->rec_len = cpu_to_le16(child->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1));
+       de->rec_len = cpu_to_le16(PAGE_SIZE - EXT2_DIR_REC_LEN(1));
        de->name_len = 2;
        strcpy (de->name, "..");
        ext2_set_de_type(dir->i_sb, de, S_IFDIR);
        
-       iops(dir)->o_brw(WRITE, sbi->osi_conn_info.conn_id, child->i_ino, child_page, 1);
-       UnlockPage(child_page);
-       page_cache_release(child_page);
-
-       child->i_nlink = 2;
-       child->i_mode = S_IFDIR | mode;
+       err = obdfs_do_writepage(inode, inode_page, IS_SYNC(inode));
+       inode->i_blocks = PAGE_SIZE/inode->i_sb->s_blocksize;
+       inode->i_size = PAGE_SIZE;
+       UnlockPage(inode_page);
+       page_cache_release(inode_page);
+       /* XXX handle err */
+
+       inode->i_nlink = 2;
+       inode->i_mode = S_IFDIR | mode;
        if (dir->i_mode & S_ISGID)
-               child->i_mode |= S_ISGID;
-       mark_inode_dirty(child);
+               inode->i_mode |= S_ISGID;
+       mark_inode_dirty(inode);
 
        /* now deal with the parent */
        page = obdfs_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
@@ -520,32 +635,29 @@ int obdfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
                goto out_no_entry;
        }
 
-       de->inode = cpu_to_le32(child->i_ino);
+       de->inode = cpu_to_le32(inode->i_ino);
        ext2_set_de_type(dir->i_sb, de, S_IFDIR);
        dir->i_version = ++event;
 
-#if 0
-       if (IS_SYNC(dir)) {
-               ll_rw_block (WRITE, 1, &bh);
-               wait_on_buffer (bh);
-       }
-#endif
        dir->i_nlink++;
        dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
        mark_inode_dirty(dir);
-       iops(dir)->o_brw(WRITE, sbi->osi_conn_info.conn_id, dir->i_ino, page, 1);
+       err = obdfs_do_writepage(dir, page, IS_SYNC(dir));
+
        UnlockPage(page);
+
        page_cache_release(page);
-       d_instantiate(dentry, child);
+       d_instantiate(dentry, inode);
        err = 0;
 out:
        EXIT;
        return err;
 
 out_no_entry:
-       child->i_nlink = 0;
-       mark_inode_dirty(child);
-       iput (child);
+       inode->i_nlink = 0;
+       mark_inode_dirty(inode);
+       iput (inode);
+       EXIT;
        goto out;
 }
 
@@ -562,7 +674,7 @@ static int empty_dir (struct inode * inode)
 
        sb = inode->i_sb;
        if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) ||
-           !(page = obdfs_getpage (inode, 0, 0, NOLOCK))) {
+           !(page = obdfs_getpage (inode, 0, 0, LOCKED))) {
                ext2_warning (inode->i_sb, "empty_dir",
                              "bad directory (dir #%lu) - no data block",
                              inode->i_ino);
@@ -582,9 +694,11 @@ static int empty_dir (struct inode * inode)
        de = (struct ext2_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len));
        while (offset < inode->i_size ) {
                if (!page || (void *) de >= (void *) (page_address(page) + PAGE_SIZE)) {
-                       if (page) 
+                       if (page) {
+                               UnlockPage(page);
                                page_cache_release(page);
-                       page = obdfs_getpage(inode, offset, 0, NOLOCK);
+                       }
+                       page = obdfs_getpage(inode, offset, 0, LOCKED);
                        if (!page) {
 #if 0
                                ext2_error (sb, "empty_dir",
@@ -596,20 +710,21 @@ static int empty_dir (struct inode * inode)
                        }
                        de = (struct ext2_dir_entry_2 *) page_address(page);
                }
-#if 0
-               if (!ext2_check_dir_entry ("empty_dir", inode, de, bh,
+               if (!obdfs_check_dir_entry ("empty_dir", inode, de, page,
                                           offset)) {
-                       brelse (bh);
+                       UnlockPage(page);
+                       page_cache_release(page);
                        return 1;
                }
-#endif
                if (le32_to_cpu(de->inode)) {
+                       UnlockPage(page);
                        page_cache_release(page);
                        return 0;
                }
                offset += le16_to_cpu(de->rec_len);
                de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
        }
+       UnlockPage(page);
        page_cache_release(page);
        return 1;
 }
@@ -620,13 +735,12 @@ int obdfs_rmdir (struct inode * dir, struct dentry *dentry)
        struct inode * inode;
        struct page *page;
        struct ext2_dir_entry_2 * de;
-       struct obdfs_sb_info *sbi;
+       int err;
 
-        ENTRY;
-       sbi = dir->i_sb->u.generic_sbp;
+       ENTRY;
 
        retval = -ENOENT;
-       page = obdfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
+       page = obdfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, LOCKED);
        if (!page)
                goto end_rmdir;
 
@@ -645,15 +759,9 @@ int obdfs_rmdir (struct inode * dir, struct dentry *dentry)
        dir->i_version = ++event;
        if (retval)
                goto end_rmdir;
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           dir->i_ino, page, 0);
+       err = obdfs_do_writepage(dir, page, IS_SYNC(dir));
        UnlockPage(page);
-#if 0
-       if (IS_SYNC(dir)) {
-               ll_rw_block (WRITE, 1, &bh);
-               wait_on_buffer (bh);
-       }
-#endif
+
        if (inode->i_nlink != 2)
                ext2_warning (inode->i_sb, "ext2_rmdir",
                              "empty directory has nlink!=2 (%d)",
@@ -671,6 +779,7 @@ int obdfs_rmdir (struct inode * dir, struct dentry *dentry)
 end_rmdir:
        if ( page )
                page_cache_release(page);
+       EXIT;
        return retval;
 }
 
@@ -680,13 +789,12 @@ int obdfs_unlink(struct inode * dir, struct dentry *dentry)
        struct inode * inode;
        struct page *page;
        struct ext2_dir_entry_2 * de;
-       struct obdfs_sb_info *sbi;
+       int err;
 
         ENTRY;
-       sbi = dir->i_sb->u.generic_sbp;
 
        retval = -ENOENT;
-       page = obdfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
+       page = obdfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, LOCKED);
        if (!page)
                goto end_unlink;
 
@@ -707,15 +815,9 @@ int obdfs_unlink(struct inode * dir, struct dentry *dentry)
        if (retval)
                goto end_unlink;
        dir->i_version = ++event;
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           dir->i_ino, page, 0);
+       err = obdfs_do_writepage(dir, page, IS_SYNC(dir));
        UnlockPage(page);
-#if 0
-       if (IS_SYNC(dir)) {
-               ll_rw_block (WRITE, 1, &bh);
-               wait_on_buffer (bh);
-       }
-#endif
+
        dir->i_ctime = dir->i_mtime = CURRENT_TIME;
        dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
        mark_inode_dirty(dir);
@@ -728,6 +830,7 @@ int obdfs_unlink(struct inode * dir, struct dentry *dentry)
 end_unlink:
        if (page)
                page_cache_release(page);
+       EXIT;
        return retval;
 }
 
@@ -735,47 +838,43 @@ int obdfs_symlink (struct inode * dir, struct dentry *dentry, const char * symna
 {
        struct ext2_dir_entry_2 * de;
        struct inode * inode;
+       struct obdfs_inode_info *oinfo;
        struct page* page = NULL, * name_page = NULL;
        char * link;
        int i, l, err = -EIO;
        char c;
-       struct obdfs_sb_info *sbi;
-       ino_t ino;
 
         ENTRY;
-
-       sbi = dir->i_sb->u.generic_sbp;
-       /*
-        * N.B. Several error exits in ext2_new_inode don't set err.
-        */
-       ino = sbi->osi_ops->o_create(sbi->osi_obd, 0, &err);
-       if ( ino == -1 ) 
-               return -1;
-       inode =  iget(dir->i_sb, ino);
-       if (!inode)
-               return err;
+       inode = obdfs_new_inode(dir);
+       oinfo = inode->u.generic_ip;
+       if ( IS_ERR(inode) ) {
+               EXIT;
+               return PTR_ERR(inode);
+       }
 
        inode->i_mode = S_IFLNK | S_IRWXUGO;
-       inode->i_op = &obdfs_inode_ops;
+       inode->i_op = &obdfs_symlink_inode_operations;
        for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
             symname [l]; l++)
                ;
-       if (l >= sizeof (inode->u.ext2_i.i_data)) {
 
-               ext2_debug ("l=%d, normal symlink\n", l);
+       if (l >= sizeof (oinfo->oi_inline)) {
+               CDEBUG(D_INODE, "l=%d, normal symlink\n", l);
 
                name_page = obdfs_getpage(inode, 0, 1, LOCKED);
                if (!name_page) {
                        inode->i_nlink--;
                        mark_inode_dirty(inode);
                        iput (inode);
+                       EXIT;
                        return err;
                }
                link = (char *)page_address(name_page);
        } else {
-               link = (char *) inode->u.ext2_i.i_data;
+               link = oinfo->oi_inline;
+               oinfo->oi_flags |= OBD_FL_INLINEDATA;
 
-               ext2_debug ("l=%d, fast symlink\n", l);
+               CDEBUG(D_INODE, "l=%d, fast symlink\n", l);
 
        }
        i = 0;
@@ -783,8 +882,7 @@ int obdfs_symlink (struct inode * dir, struct dentry *dentry, const char * symna
                link[i++] = c;
        link[i] = 0;
        if (name_page) {
-               sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                                   inode->i_ino, name_page, 1);
+               obdfs_do_writepage(inode, name_page, IS_SYNC(inode));
                PDEBUG(name_page, "symlink");
                UnlockPage(name_page);
                page_cache_release(name_page);
@@ -798,18 +896,13 @@ int obdfs_symlink (struct inode * dir, struct dentry *dentry, const char * symna
        de->inode = cpu_to_le32(inode->i_ino);
        ext2_set_de_type(dir->i_sb, de, S_IFLNK);
        dir->i_version = ++event;
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           dir->i_ino, page, 1);
+       obdfs_do_writepage(dir, page, IS_SYNC(dir));
        UnlockPage(page);
-#if 0
-       if (IS_SYNC(dir)) {
-               ll_rw_block (WRITE, 1, &bh);
-               wait_on_buffer (bh);
-       }
-#endif
+
        d_instantiate(dentry, inode);
        err = 0;
 out:
+       EXIT;
        return err;
 
 out_no_entry:
@@ -826,10 +919,8 @@ int obdfs_link (struct dentry * old_dentry,
        struct ext2_dir_entry_2 * de;
        struct page *page;
        int err;
-       struct obdfs_sb_info *sbi;
 
         ENTRY;
-       sbi = dir->i_sb->u.generic_sbp;
 
        if (S_ISDIR(inode->i_mode))
                return -EPERM;
@@ -844,16 +935,10 @@ int obdfs_link (struct dentry * old_dentry,
        de->inode = cpu_to_le32(inode->i_ino);
        ext2_set_de_type(dir->i_sb, de, inode->i_mode);
        dir->i_version = ++event;
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           dir->i_ino, page, 0);
+
+       obdfs_do_writepage(dir, page, IS_SYNC(dir));
        UnlockPage(page);
 
-#if 0
-       if (IS_SYNC(dir)) {
-               ll_rw_block (WRITE, 1, &bh);
-               wait_on_buffer (bh);
-       }
-#endif
        page_cache_release(page);
        inode->i_nlink++;
        inode->i_ctime = CURRENT_TIME;
@@ -872,20 +957,20 @@ int obdfs_link (struct dentry * old_dentry,
  * higher-level routines.
  */
 int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry,
-                          struct inode * new_dir,struct dentry *new_dentry)
+                          struct inode * new_dir, struct dentry *new_dentry)
 {
        struct inode * old_inode, * new_inode;
        struct page * old_page, * new_page, * dir_page;
        struct ext2_dir_entry_2 * old_de, * new_de;
        int retval;
-       struct obdfs_sb_info *sbi;
 
         ENTRY;
-       sbi = old_dir->i_sb->u.generic_sbp;
 
        new_page = dir_page = NULL;
 
-       old_page = obdfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de);
+       /* does the old entry exist? - if not get out */
+       old_page = obdfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de, NOLOCK);
+       PDEBUG(old_page, "rename - old page");
        /*
         *  Check for inode number is _not_ due to possible IO errors.
         *  We might rmdir the source, keep it as pwd of some process
@@ -897,9 +982,11 @@ int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry,
        if (!old_page || le32_to_cpu(old_de->inode) != old_inode->i_ino)
                goto end_rename;
 
+       /* find new inode */
        new_inode = new_dentry->d_inode;
        new_page = obdfs_find_entry (new_dir, new_dentry->d_name.name,
-                               new_dentry->d_name.len, &new_de);
+                               new_dentry->d_name.len, &new_de, NOLOCK);
+       PDEBUG(new_page, "rename - new page ");
        if (new_page) {
                if (!new_inode) {
                        page_cache_release(new_page);
@@ -908,7 +995,9 @@ int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry,
                        DQUOT_INIT(new_inode);
                }
        }
+       /* in this case we to check more ... */
        if (S_ISDIR(old_inode->i_mode)) {
+               /* can only rename into empty new directory */
                if (new_inode) {
                        retval = -ENOTEMPTY;
                        if (!empty_dir (new_inode))
@@ -916,6 +1005,8 @@ int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry,
                }
                retval = -EIO;
                dir_page= obdfs_getpage (old_inode, 0, 0, LOCKED);
+               PDEBUG(dir_page, "rename dir page");
+
                if (!dir_page)
                        goto end_rename;
                if (le32_to_cpu(PARENT_INO(page_address(dir_page))) != old_dir->i_ino)
@@ -925,17 +1016,19 @@ int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry,
                                new_dir->i_nlink >= EXT2_LINK_MAX)
                        goto end_rename;
        }
+       /* create the target dir entry */
        if (!new_page) {
                new_page = obdfs_add_entry (new_dir, new_dentry->d_name.name,
                                        new_dentry->d_name.len, &new_de,
                                        &retval);
+               PDEBUG(new_page, "rename new page");
                if (!new_page)
                        goto end_rename;
        }
        new_dir->i_version = ++event;
 
        /*
-        * ok, that's it
+        * remove the old entry
         */
        new_de->inode = le32_to_cpu(old_inode->i_ino);
        if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
@@ -955,8 +1048,7 @@ int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry,
        mark_inode_dirty(old_dir);
        if (dir_page) {
                PARENT_INO(page_address(dir_page)) = le32_to_cpu(new_dir->i_ino);
-               sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           new_dir->i_ino, dir_page, 0);
+               obdfs_do_writepage(old_inode, dir_page, IS_SYNC(old_inode));
                old_dir->i_nlink--;
                mark_inode_dirty(old_dir);
                if (new_inode) {
@@ -968,31 +1060,34 @@ int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry,
                        mark_inode_dirty(new_dir);
                }
        }
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           old_inode->i_ino, old_page, 0);
-#if 0
-       if (IS_SYNC(old_dir)) {
-               ll_rw_block (WRITE, 1, &old_bh);
-               wait_on_buffer (old_bh);
-       }
-#endif
-       sbi->osi_ops->o_brw(WRITE, sbi->osi_conn_info.conn_id, 
-                           new_inode->i_ino, new_page, 0);
-#if 0
-       if (IS_SYNC(new_dir)) {
-               ll_rw_block (WRITE, 1, &new_bh);
-               wait_on_buffer (new_bh);
+       if ( old_page != new_page ) {
+               unsigned long index = old_page->index;
+               /* lock the old_page and release unlocked copy */
+               CDEBUG(D_INODE, "old_page at %p\n", old_page);
+               page_cache_release(old_page);
+               old_page = obdfs_getpage(old_dir, index >> PAGE_SHIFT, 0, LOCKED);
+               CDEBUG(D_INODE, "old_page at %p\n", old_page);
+               obdfs_do_writepage(old_dir, old_page, IS_SYNC(old_dir));
        }
-#endif
+
+       obdfs_do_writepage(new_dir, new_page, IS_SYNC(new_dir));
 
        retval = 0;
 
 end_rename:
+       if (old_page && PageLocked(old_page) )
+               UnlockPage(old_page);
        if (old_page)
                page_cache_release(old_page);
+       if (new_page && PageLocked(new_page) )
+               UnlockPage(new_page);
        if (new_page)
                page_cache_release(new_page);
+       if (dir_page && PageLocked(dir_page) )
+               UnlockPage(dir_page);
        if (dir_page)
                page_cache_release(dir_page);
+
+
        return retval;
 }