patch-2.4.15 linux/fs/intermezzo/vfs.c

Next file: linux/fs/isofs/compress.c
Previous file: linux/fs/intermezzo/upcall.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.14/linux/fs/intermezzo/vfs.c linux/fs/intermezzo/vfs.c
@@ -0,0 +1,2304 @@
+/*
+ * vfs.c
+ *
+ * This file implements kernel downcalls from lento.
+ *
+ * Author: Rob Simmonds <simmonds@stelias.com>
+ *         Andreas Dilger <adilger@stelias.com>
+ * Copyright (C) 2000 Stelias Computing Inc
+ * Copyright (C) 2000 Red Hat Inc.
+ *
+ * Extended attribute support
+ * Copyright (C) 2001 Shirish H. Phatak, Tacit Networks, Inc.
+ *
+ * This code is based on code from namei.c in the linux file system;
+ * see copyright notice below.
+ */
+
+/** namei.c copyright **/
+
+/*
+ *  linux/fs/namei.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * Some corrections by tytso.
+ */
+
+/* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname
+ * lookup logic.
+ */
+
+/** end of namei.c copyright **/
+
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+#include <asm/semaphore.h>
+#include <asm/pgtable.h>
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/blk.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_upcall.h>
+#include <linux/intermezzo_psdev.h>
+#include <linux/intermezzo_kml.h>
+
+#ifdef CONFIG_FS_EXT_ATTR
+#include <linux/ext_attr.h>
+
+#ifdef CONFIG_FS_POSIX_ACL
+#include <linux/posix_acl.h>
+#endif
+#endif
+
+extern struct inode_operations presto_sym_iops;
+
+/*
+ * It's inline, so penalty for filesystems that don't use sticky bit is
+ * minimal.
+ */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+        if (!(dir->i_mode & S_ISVTX))
+                return 0;
+        if (inode->i_uid == current->fsuid)
+                return 0;
+        if (dir->i_uid == current->fsuid)
+                return 0;
+        return !capable(CAP_FOWNER);
+}
+
+/* from linux/fs/namei.c */
+static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir)
+{
+        int error;
+        if (!victim->d_inode || victim->d_parent->d_inode != dir)
+                return -ENOENT;
+        error = permission(dir,MAY_WRITE | MAY_EXEC);
+        if (error)
+                return error;
+        if (IS_APPEND(dir))
+                return -EPERM;
+        if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
+            IS_IMMUTABLE(victim->d_inode))
+                return -EPERM;
+        if (isdir) {
+                if (!S_ISDIR(victim->d_inode->i_mode))
+                        return -ENOTDIR;
+                if (IS_ROOT(victim))
+                        return -EBUSY;
+        } else if (S_ISDIR(victim->d_inode->i_mode))
+                return -EISDIR;
+        return 0;
+}
+
+/* from linux/fs/namei.c */
+static inline int may_create(struct inode *dir, struct dentry *child) {
+        if (child->d_inode)
+                return -EEXIST;
+        if (IS_DEADDIR(dir))
+                return -ENOENT;
+        return permission(dir,MAY_WRITE | MAY_EXEC);
+}
+
+#ifdef PRESTO_DEBUG
+/* The loop_discard_io() function is available via a kernel patch to the
+ * loop block device.  It "works" by accepting writes, but throwing them
+ * away, rather than trying to write them to disk.  The old method worked
+ * by setting the underlying device read-only, but that has the problem
+ * that dirty buffers are kept in memory, and ext3 didn't like that at all.
+ */
+#ifdef CONFIG_LOOP_DISCARD
+#define BLKDEV_FAIL(dev,fail) loop_discard_io(dev,fail)
+#else
+#define BLKDEV_FAIL(dev,fail) set_device_ro(dev, 1)
+#endif
+
+/* If a breakpoint has been set via /proc/sys/intermezzo/intermezzoX/errorval,
+ * that is the same as "value", the underlying device will "fail" now.
+ */
+inline void presto_debug_fail_blkdev(struct presto_file_set *fset,
+                                     unsigned long value)
+{
+        int minor = presto_f2m(fset);
+        int errorval = upc_comms[minor].uc_errorval;
+        kdev_t dev = fset->fset_mtpt->d_inode->i_dev;
+
+        if (errorval && errorval == (long)value && !is_read_only(dev)) {
+                CDEBUG(D_SUPER, "setting device %s read only\n", kdevname(dev));
+                BLKDEV_FAIL(dev, 1);
+                upc_comms[minor].uc_errorval = -dev;
+        }
+}
+#else
+#define presto_debug_fail_blkdev(dev,value) do {} while (0)
+#endif
+
+
+static inline int presto_do_kml(struct lento_vfs_context *info, struct inode* inode)
+{
+        if ( ! (info->flags & LENTO_FL_KML) ) 
+                return 0;
+        if ( inode->i_gid == presto_excluded_gid ) 
+                return 0;
+        return 1;
+}
+
+static inline int presto_do_expect(struct lento_vfs_context *info, struct inode *inode)
+{
+        if ( ! (info->flags & LENTO_FL_EXPECT) ) 
+                return 0;
+        if ( inode->i_gid == presto_excluded_gid ) 
+                return 0;
+        return 1;
+}
+
+
+/* XXX fixme: this should not fail, all these dentries are in memory
+   when _we_ call this */
+int presto_settime(struct presto_file_set *fset, 
+                   struct dentry *dentry, 
+                   struct lento_vfs_context *ctx, 
+                   int valid)
+{
+        int error; 
+        struct inode *inode = dentry->d_inode;
+        struct inode_operations *iops;
+        struct iattr iattr;
+
+        ENTRY;
+        if (ctx->flags &  LENTO_FL_IGNORE_TIME ) { 
+                EXIT;
+                return 0;
+        }
+        iattr.ia_ctime = ctx->updated_time;
+        iattr.ia_mtime = ctx->updated_time;
+        iattr.ia_valid = valid;
+
+        error = -EROFS;
+        if (IS_RDONLY(inode)) {
+                EXIT;
+                return -EROFS;
+        }
+
+        if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+                EXIT;
+                return -EPERM;
+        }
+
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter); 
+        if (!iops) { 
+                EXIT;
+                return error;
+        }
+
+        if (iops->setattr != NULL)
+                error = iops->setattr(dentry, &iattr);
+        else {
+		error = 0;
+                inode_setattr(dentry->d_inode, &iattr);
+	}
+        EXIT;
+        return error;
+}
+
+
+int presto_do_setattr(struct presto_file_set *fset, struct dentry *dentry,
+                      struct iattr *iattr, struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        struct inode *inode = dentry->d_inode;
+        struct inode_operations *iops;
+        int error;
+        struct presto_version old_ver, new_ver;
+        void *handle;
+	off_t old_size=inode->i_size;
+
+        ENTRY;
+        error = -EROFS;
+        if (IS_RDONLY(inode)) {
+                EXIT;
+                return -EROFS;
+        }
+
+        if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+                EXIT;
+                return -EPERM;
+        }
+
+        presto_getversion(&old_ver, dentry->d_inode);
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter); 
+        if (!iops &&
+            !iops->setattr) {
+                EXIT;
+                return error;
+        }
+
+	error = presto_reserve_space(fset->fset_cache, 2*PRESTO_REQHIGH); 
+	if (error) {
+		EXIT;
+		return error;
+	}
+
+	if  (iattr->ia_valid & ATTR_SIZE) { 
+		handle = presto_trans_start(fset, dentry->d_inode, PRESTO_OP_TRUNC);
+	} else {
+		handle = presto_trans_start(fset, dentry->d_inode, PRESTO_OP_SETATTR);
+	}
+
+        if ( IS_ERR(handle) ) {
+                printk("presto_do_setattr: no space for transaction\n");
+		presto_release_space(fset->fset_cache, 2*PRESTO_REQHIGH); 
+                return -ENOSPC;
+        }
+
+        if (dentry->d_inode && iops->setattr) {
+                error = iops->setattr(dentry, iattr);
+        } else {
+                error = inode_change_ok(dentry->d_inode, iattr);
+                if (!error) 
+                        inode_setattr(inode, iattr);
+        }
+
+	if (!error && (iattr->ia_valid & ATTR_SIZE))
+		vmtruncate(inode, iattr->ia_size);
+
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SETATTR | 0x10);
+
+        if ( presto_do_kml(info, dentry->d_inode) ) {
+                if ((iattr->ia_valid & ATTR_SIZE) && (old_size != inode->i_size)) {
+                	struct file file;
+                	/* Journal a close whenever we see a potential truncate
+                 	* At the receiving end, lento should explicitly remove
+                 	* ATTR_SIZE from the list of valid attributes */
+                	presto_getversion(&new_ver, inode);
+                	file.private_data = NULL;
+                	file.f_dentry = dentry;
+                	error=presto_journal_close(&rec, fset, &file, dentry, &new_ver);
+            	}
+
+		if (!error)
+                	error = presto_journal_setattr(&rec, fset, dentry, &old_ver, iattr);
+        }
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SETATTR | 0x20);
+        if ( presto_do_expect(info, dentry->d_inode) )
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SETATTR | 0x30);
+
+        EXIT;
+exit:
+	presto_release_space(fset->fset_cache, 2*PRESTO_REQHIGH); 
+        presto_trans_commit(fset, handle);
+        return error;
+}
+
+int lento_setattr(const char *name, struct iattr *iattr,
+                  struct lento_vfs_context *info)
+{
+        struct nameidata nd;
+        struct dentry *dentry;
+        struct presto_file_set *fset;
+        int error;
+#ifdef  CONFIG_FS_POSIX_ACL
+        int (*set_posix_acl)(struct inode *, int type, posix_acl_t *)=NULL;
+#endif
+
+        ENTRY;
+        CDEBUG(D_PIOCTL,"name %s, valid %#x, mode %#o, uid %d, gid %d, size %Ld\n",
+               name, iattr->ia_valid, iattr->ia_mode, iattr->ia_uid,
+               iattr->ia_gid, iattr->ia_size);
+        CDEBUG(D_PIOCTL, "atime %#lx, mtime %#lx, ctime %#lx, attr_flags %#x\n",
+               iattr->ia_atime, iattr->ia_mtime, iattr->ia_ctime,
+               iattr->ia_attr_flags);
+        CDEBUG(D_PIOCTL, "offset %d, recno %d, flags %#x\n",
+               info->slot_offset, info->recno, info->flags);
+
+        lock_kernel();
+        error = presto_walk(name, &nd);
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+        dentry = nd.dentry;
+        
+        fset = presto_fset(dentry);
+        error = -EINVAL;
+        if ( !fset ) {
+                printk("No fileset!\n");
+                EXIT;
+                goto exit_lock;
+        }
+
+        /* NOTE: this prevents us from changing the filetype on setattr,
+         *       as we normally only want to change permission bits.
+         *       If this is not correct, then we need to fix the perl code
+         *       to always send the file type OR'ed with the permission.
+         */
+        if (iattr->ia_valid & ATTR_MODE) {
+                int set_mode = iattr->ia_mode;
+                iattr->ia_mode = (iattr->ia_mode & S_IALLUGO) |
+                                 (dentry->d_inode->i_mode & ~S_IALLUGO);
+                CDEBUG(D_PIOCTL, "chmod: orig %#o, set %#o, result %#o\n",
+                       dentry->d_inode->i_mode, set_mode, iattr->ia_mode);
+#ifdef CONFIG_FS_POSIX_ACL
+                /* ACl code interacts badly with setattr 
+                 * since it tries to modify the ACL using 
+                 * set_ext_attr which recurses back into presto.  
+                 * This only happens if ATTR_MODE is set.
+                 * Here we are doing a "forced" mode set 
+                 * (initiated by lento), so we disable the 
+                 * set_posix_acl operation which 
+                 * prevents such recursion.  -SHP
+                 *
+                 * This will probably still be required when native
+                 * acl journalling is in place.
+                 */
+                set_posix_acl=dentry->d_inode->i_op->set_posix_acl;
+                dentry->d_inode->i_op->set_posix_acl=NULL;
+#endif
+        }
+
+        error = presto_do_setattr(fset, dentry, iattr, info);
+
+#ifdef CONFIG_FS_POSIX_ACL
+        /* restore the inode_operations if we changed them*/
+        if (iattr->ia_valid & ATTR_MODE) 
+                dentry->d_inode->i_op->set_posix_acl=set_posix_acl;
+#endif
+
+
+        EXIT;
+exit_lock:
+        path_release(&nd);
+exit:
+        unlock_kernel();
+        return error;
+}
+
+int presto_do_create(struct presto_file_set *fset, struct dentry *dir,
+                     struct dentry *dentry, int mode,
+                     struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error;
+        struct presto_version tgt_dir_ver, new_file_ver;
+        struct inode_operations *iops;
+        void *handle;
+
+        ENTRY;
+        mode &= S_IALLUGO;
+        mode |= S_IFREG;
+
+        down(&dir->d_inode->i_zombie);
+	error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH); 
+	if (error) {
+		EXIT;
+        	up(&dir->d_inode->i_zombie);
+		return error;
+	}
+
+        error = may_create(dir->d_inode, dentry);
+        if (error) {
+                EXIT;
+                goto exit_pre_lock;
+        }
+
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+        if (!iops->create) {
+                EXIT;
+                goto exit_pre_lock;
+        }
+
+        presto_getversion(&tgt_dir_ver, dir->d_inode);
+        handle = presto_trans_start(fset, dir->d_inode, PRESTO_OP_CREATE);
+        if ( IS_ERR(handle) ) {
+                EXIT;
+		presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+                printk("presto_do_create: no space for transaction\n");
+                error=-ENOSPC;
+		goto exit_pre_lock;
+        }
+        DQUOT_INIT(dir->d_inode);
+        lock_kernel();
+        error = iops->create(dir->d_inode, dentry, mode);
+        if (error) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        if (dentry->d_inode && 
+            dentry->d_inode->i_gid != presto_excluded_gid) {
+                struct presto_cache *cache = fset->fset_cache;
+                /* was this already done? */
+                presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+                filter_setup_dentry_ops(cache->cache_filter, 
+                                        dentry->d_op, 
+                                        &presto_dentry_ops);
+                dentry->d_op = filter_c2udops(cache->cache_filter);
+
+                /* if Lento creates this file, we won't have data */
+                if ( ISLENTO(presto_c2m(cache)) ) {
+                        presto_set(dentry, PRESTO_ATTR);
+                } else {
+                        presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+                }
+        }
+
+        error = presto_settime(fset, dir, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit_lock;
+        }
+        error = presto_settime(fset, dentry, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit_lock;
+        }
+
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_CREATE | 0x10);
+        presto_getversion(&new_file_ver, dentry->d_inode);
+        if ( presto_do_kml(info, dentry->d_inode) )
+                error = presto_journal_create(&rec, fset, dentry, &tgt_dir_ver,
+                                              &new_file_ver, 
+					      dentry->d_inode->i_mode);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_CREATE | 0x20);
+        if ( presto_do_expect(info, dentry->d_inode) )
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_CREATE | 0x30);
+        EXIT;
+
+ exit_lock:
+        unlock_kernel();
+        presto_trans_commit(fset, handle);
+ exit_pre_lock:
+	presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+        up(&dir->d_inode->i_zombie);
+        return error;
+}
+
+/* from namei.c */
+static struct dentry *lookup_create(struct nameidata *nd, int is_dir)
+{
+        struct dentry *dentry;
+
+        down(&nd->dentry->d_inode->i_sem);
+        dentry = ERR_PTR(-EEXIST);
+        if (nd->last_type != LAST_NORM)
+                goto fail;
+        dentry = lookup_hash(&nd->last, nd->dentry);
+        if (IS_ERR(dentry))
+                goto fail;
+        if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
+                goto enoent;
+        return dentry;
+enoent:
+        dput(dentry);
+        dentry = ERR_PTR(-ENOENT);
+fail:
+        return dentry;
+}
+
+int lento_create(const char *name, int mode, struct lento_vfs_context *info)
+{
+        int error;
+        struct nameidata nd;
+        char * pathname;
+        struct dentry *dentry;
+        struct presto_file_set *fset;
+
+        ENTRY;
+        pathname = getname(name);
+        error = PTR_ERR(pathname);
+        if (IS_ERR(pathname)) {
+                EXIT;
+                goto exit;
+        }
+
+        /* this looks up the parent */
+//        if (path_init(pathname, LOOKUP_FOLLOW | LOOKUP_POSITIVE, &nd))
+        if (path_init(pathname,  LOOKUP_PARENT, &nd))
+                error = path_walk(pathname, &nd);
+        if (error) {
+		EXIT;
+                goto exit;
+	}
+        dentry = lookup_create(&nd, 0);
+        error = PTR_ERR(dentry);
+        if (IS_ERR(dentry)) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        fset = presto_fset(dentry);
+        error = -EINVAL;
+        if ( !fset ) {
+                printk("No fileset!\n");
+                EXIT;
+                goto exit_lock;
+        }
+        error = presto_do_create(fset, dentry->d_parent, dentry, (mode&S_IALLUGO)|S_IFREG,
+                                 info);
+
+        EXIT;
+
+ exit_lock:
+        path_release (&nd);
+	dput(dentry); 
+        up(&dentry->d_parent->d_inode->i_sem);
+        putname(pathname);
+exit:
+        return error;
+}
+
+int presto_do_link(struct presto_file_set *fset, struct dentry *old_dentry,
+                   struct dentry *dir, struct dentry *new_dentry,
+                   struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        struct inode *inode;
+        int error;
+        struct inode_operations *iops;
+        struct presto_version tgt_dir_ver;
+        struct presto_version new_link_ver;
+        void *handle;
+
+        down(&dir->d_inode->i_zombie);
+	error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH); 
+	if (error) {
+		EXIT;
+        	up(&dir->d_inode->i_zombie);
+		return error;
+	}
+        error = -ENOENT;
+        inode = old_dentry->d_inode;
+        if (!inode)
+                goto exit_lock;
+
+        error = may_create(dir->d_inode, new_dentry);
+        if (error)
+                goto exit_lock;
+
+        error = -EXDEV;
+        if (dir->d_inode->i_dev != inode->i_dev)
+                goto exit_lock;
+
+        /*
+         * A link to an append-only or immutable file cannot be created.
+         */
+        error = -EPERM;
+        if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+        if (!iops->link) {
+                EXIT;
+                goto exit_lock;
+        }
+
+
+        presto_getversion(&tgt_dir_ver, dir->d_inode);
+        handle = presto_trans_start(fset, dir->d_inode, PRESTO_OP_LINK);
+        if ( IS_ERR(handle) ) {
+		presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+                printk("presto_do_link: no space for transaction\n");
+                return -ENOSPC;
+        }
+
+        DQUOT_INIT(dir->d_inode);
+        lock_kernel();
+        error = iops->link(old_dentry, dir->d_inode, new_dentry);
+        unlock_kernel();
+        if (error) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        error = presto_settime(fset, dir, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit_lock;
+        }
+        error = presto_settime(fset, new_dentry, info, ATTR_CTIME);
+        if (error) { 
+                EXIT;
+                goto exit_lock;
+        }
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_LINK | 0x10);
+        presto_getversion(&new_link_ver, new_dentry->d_inode);
+        if ( presto_do_kml(info, old_dentry->d_inode) )
+                error = presto_journal_link(&rec, fset, old_dentry, new_dentry,
+                                            &tgt_dir_ver, &new_link_ver);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_LINK | 0x20);
+        if ( presto_do_expect(info, old_dentry->d_inode) )
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_LINK | 0x30);
+        EXIT;
+        presto_trans_commit(fset, handle);
+exit_lock:
+	presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+        up(&dir->d_inode->i_zombie);
+        return error;
+}
+
+
+int lento_link(const char * oldname, const char * newname, 
+                         struct lento_vfs_context *info)
+{
+        int error;
+        char * from;
+        char * to;
+        struct presto_file_set *fset;
+
+        from = getname(oldname);
+        if(IS_ERR(from))
+                return PTR_ERR(from);
+        to = getname(newname);
+        error = PTR_ERR(to);
+        if (!IS_ERR(to)) {
+                struct dentry *new_dentry;
+                struct nameidata nd, old_nd;
+
+                error = 0;
+                if (path_init(from, LOOKUP_POSITIVE, &old_nd))
+                        error = path_walk(from, &old_nd);
+                if (error)
+                        goto exit;
+                if (path_init(to, LOOKUP_PARENT, &nd))
+                        error = path_walk(to, &nd);
+                if (error)
+                        goto out;
+                error = -EXDEV;
+                if (old_nd.mnt != nd.mnt)
+                        goto out;
+                new_dentry = lookup_create(&nd, 0);
+                error = PTR_ERR(new_dentry);
+
+                if (!IS_ERR(new_dentry)) {
+                        fset = presto_fset(new_dentry);
+                        error = -EINVAL;
+                        if ( !fset ) {
+                                printk("No fileset!\n");
+                                EXIT;
+                                goto out2;
+                        }
+                        error = presto_do_link(fset, old_nd.dentry, 
+                                               nd.dentry,
+                                               new_dentry, info);
+                        dput(new_dentry);
+                }
+        out2:
+                up(&nd.dentry->d_inode->i_sem);
+                path_release(&nd);
+        out:
+                path_release(&old_nd);
+        exit:
+                putname(to);
+        }
+        putname(from);
+
+        return error;
+}
+
+
+int presto_do_unlink(struct presto_file_set *fset, struct dentry *dir,
+                     struct dentry *dentry, struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error;
+        struct inode_operations *iops;
+        struct presto_version tgt_dir_ver, old_file_ver;
+        void *handle;
+        int do_kml = 0, do_expect =0;
+	int linkno = 0;
+        ENTRY;
+        down(&dir->d_inode->i_zombie);
+        error = may_delete(dir->d_inode, dentry, 0);
+        if (error) {
+                EXIT;
+                up(&dir->d_inode->i_zombie);
+                return error;
+        }
+
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+        if (!iops->unlink) {
+                EXIT;
+                up(&dir->d_inode->i_zombie);
+                return error;
+        }
+
+	error = presto_reserve_space(fset->fset_cache, PRESTO_REQLOW); 
+	if (error) {
+		EXIT;
+        	up(&dir->d_inode->i_zombie);
+		return error;
+	}
+
+        presto_getversion(&tgt_dir_ver, dir->d_inode);
+        presto_getversion(&old_file_ver, dentry->d_inode);
+        handle = presto_trans_start(fset, dir->d_inode, PRESTO_OP_UNLINK);
+        if ( IS_ERR(handle) ) {
+		presto_release_space(fset->fset_cache, PRESTO_REQLOW); 
+                printk("ERROR: presto_do_unlink: no space for transaction. Tell Peter.\n");
+                up(&dir->d_inode->i_zombie);
+                return -ENOSPC;
+        }
+        DQUOT_INIT(dir->d_inode);
+        if (d_mountpoint(dentry))
+                error = -EBUSY;
+        else {
+                lock_kernel();
+		linkno = dentry->d_inode->i_nlink;
+		if (linkno > 1) {
+			dget(dentry);
+		}
+                do_kml = presto_do_kml(info, dir->d_inode);
+                do_expect = presto_do_expect(info, dir->d_inode);
+                error = iops->unlink(dir->d_inode, dentry);
+                unlock_kernel();
+                if (!error)
+                        d_delete(dentry);
+        }
+
+        if (linkno > 1) { 
+                error = presto_settime(fset, dentry, info, ATTR_CTIME);
+                dput(dentry); 
+                if (error) { 
+                        EXIT;
+                        goto exit;
+                }
+        }
+
+        error = presto_settime(fset, dir, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+
+        up(&dir->d_inode->i_zombie);
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_UNLINK | 0x10);
+        if ( do_kml ) { 
+                error = presto_journal_unlink(&rec, fset, dir, &tgt_dir_ver,
+                                              &old_file_ver,
+                                              dentry->d_name.len,
+                                              dentry->d_name.name);
+	}
+        presto_debug_fail_blkdev(fset, PRESTO_OP_UNLINK | 0x20);
+        if ( do_expect ) { 
+                error = presto_write_last_rcvd(&rec, fset, info);
+	}
+        presto_debug_fail_blkdev(fset, PRESTO_OP_UNLINK | 0x30);
+        EXIT;
+exit:
+	presto_release_space(fset->fset_cache, PRESTO_REQLOW); 
+        presto_trans_commit(fset, handle);
+        return error;
+}
+
+
+int lento_unlink(const char *pathname, struct lento_vfs_context *info)
+{
+        int error = 0;
+        char * name;
+        struct dentry *dentry;
+        struct nameidata nd;
+        struct presto_file_set *fset;
+
+        ENTRY;
+
+        name = getname(pathname);
+        if(IS_ERR(name))
+                return PTR_ERR(name);
+
+        if (path_init(name, LOOKUP_PARENT, &nd))
+                error = path_walk(name, &nd);
+        if (error)
+                goto exit;
+        error = -EISDIR;
+        if (nd.last_type != LAST_NORM)
+                goto exit1;
+        down(&nd.dentry->d_inode->i_sem);
+        dentry = lookup_hash(&nd.last, nd.dentry);
+        error = PTR_ERR(dentry);
+        if (!IS_ERR(dentry)) {
+                fset = presto_fset(dentry);
+                error = -EINVAL;
+                if ( !fset ) {
+                        printk("No fileset!\n");
+                        EXIT;
+                        goto exit2;
+                }
+                /* Why not before? Because we want correct error value */
+                if (nd.last.name[nd.last.len])
+                        goto slashes;
+                error = presto_do_unlink(fset, nd.dentry, dentry, info);
+        exit2:
+                EXIT;
+                dput(dentry);
+        }
+        up(&nd.dentry->d_inode->i_sem);
+exit1:
+        path_release(&nd);
+exit:
+        putname(name);
+
+        return error;
+
+slashes:
+        error = !dentry->d_inode ? -ENOENT :
+                S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+        goto exit2;
+}
+
+int presto_do_symlink(struct presto_file_set *fset, struct dentry *dir,
+                      struct dentry *dentry, const char *oldname,
+                      struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error;
+        struct presto_version tgt_dir_ver, new_link_ver;
+        struct inode_operations *iops;
+        void *handle;
+
+        ENTRY;
+        down(&dir->d_inode->i_zombie);
+	/* record + max path len + space to free */ 
+	error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH + 4096); 
+	if (error) {
+		EXIT;
+        	up(&dir->d_inode->i_zombie);
+		return error;
+	}
+
+        error = may_create(dir->d_inode, dentry);
+        if (error) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+        if (!iops->symlink) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        presto_getversion(&tgt_dir_ver, dir->d_inode);
+        handle = presto_trans_start(fset, dir->d_inode, PRESTO_OP_SYMLINK);
+        if ( IS_ERR(handle) ) {
+		presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096); 
+                printk("ERROR: presto_do_symlink: no space for transaction. Tell Peter.\n"); 
+                EXIT;
+                return -ENOSPC;
+        }
+        DQUOT_INIT(dir->d_inode);
+        lock_kernel();
+        error = iops->symlink(dir->d_inode, dentry, oldname);
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+
+        if (dentry->d_inode &&
+            dentry->d_inode->i_gid != presto_excluded_gid) {
+                struct presto_cache *cache = fset->fset_cache;
+                
+                presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+                filter_setup_dentry_ops(cache->cache_filter, dentry->d_op, 
+                                        &presto_dentry_ops);
+                dentry->d_op = filter_c2udops(cache->cache_filter);
+                /* XXX ? Cache state ? if Lento creates a symlink */
+                if ( ISLENTO(presto_c2m(cache)) ) {
+                        presto_set(dentry, PRESTO_ATTR);
+                } else {
+                        presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+                }
+        }
+
+        error = presto_settime(fset, dir, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+        error = presto_settime(fset, dentry, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SYMLINK | 0x10);
+        presto_getversion(&new_link_ver, dentry->d_inode);
+        if ( presto_do_kml(info, dentry->d_inode) )
+                error = presto_journal_symlink(&rec, fset, dentry, oldname,
+                                               &tgt_dir_ver, &new_link_ver);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SYMLINK | 0x20);
+        if ( presto_do_expect(info, dentry->d_inode) )
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SYMLINK | 0x30);
+        EXIT;
+exit:
+        unlock_kernel();
+        presto_trans_commit(fset, handle);
+ exit_lock:
+	presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096); 
+        up(&dir->d_inode->i_zombie);
+        return error;
+}
+
+int lento_symlink(const char *oldname, const char *newname,
+                  struct lento_vfs_context *info)
+{
+        int error;
+        char *from;
+        char *to;
+        struct dentry *dentry;
+        struct presto_file_set *fset;
+        struct nameidata nd;
+
+        ENTRY;
+        lock_kernel();
+        from = getname(oldname);
+        error = PTR_ERR(from);
+        if (IS_ERR(from)) {
+                EXIT;
+                goto exit;
+        }
+
+        to = getname(newname);
+        error = PTR_ERR(to);
+        if (IS_ERR(to)) {
+                EXIT;
+                goto exit_from;
+        }
+
+        if (path_init(to, LOOKUP_PARENT, &nd)) 
+                error = path_walk(to, &nd);
+        if (error) {
+                EXIT;
+                goto exit_to;
+        }
+
+        dentry = lookup_create(&nd, 0);
+        error = PTR_ERR(dentry);
+        if (IS_ERR(dentry)) {
+        	path_release(&nd);
+                EXIT;
+                goto exit_to;
+        }
+
+        fset = presto_fset(dentry);
+        error = -EINVAL;
+        if ( !fset ) {
+                printk("No fileset!\n");
+        	path_release(&nd);
+                EXIT;
+                goto exit_lock;
+        }
+        error = presto_do_symlink(fset, nd.dentry,
+                                  dentry, oldname, info);
+        path_release(&nd);
+        EXIT;
+ exit_lock:
+        up(&nd.dentry->d_inode->i_sem);
+        dput(dentry);
+ exit_to:
+        putname(to);
+ exit_from:
+        putname(from);
+ exit:
+        unlock_kernel();
+        return error;
+}
+
+int presto_do_mkdir(struct presto_file_set *fset, struct dentry *dir,
+                    struct dentry *dentry, int mode,
+                    struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error;
+        struct presto_version tgt_dir_ver, new_dir_ver;
+        void *handle;
+
+        ENTRY;
+        down(&dir->d_inode->i_zombie);
+	/* one journal record + directory block + room for removals*/ 
+	error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH + 4096); 
+	if (error) { 
+                EXIT;
+        	up(&dir->d_inode->i_zombie);
+                return error;
+        }
+
+        error = may_create(dir->d_inode, dentry);
+        if (error) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        error = -EPERM;
+        if (!filter_c2cdiops(fset->fset_cache->cache_filter)->mkdir) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        error = -ENOSPC;
+        presto_getversion(&tgt_dir_ver, dir->d_inode);
+        handle = presto_trans_start(fset, dir->d_inode, PRESTO_OP_MKDIR);
+        if ( IS_ERR(handle) ) {
+		presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096); 
+                printk("presto_do_mkdir: no space for transaction\n");
+                goto exit_lock;
+        }
+
+        DQUOT_INIT(dir->d_inode);
+        mode &= (S_IRWXUGO|S_ISVTX);
+        lock_kernel();
+        error = filter_c2cdiops(fset->fset_cache->cache_filter)->mkdir(dir->d_inode, dentry, mode);
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+
+        if ( dentry->d_inode && !error && 
+             dentry->d_inode->i_gid != presto_excluded_gid) {
+                struct presto_cache *cache = fset->fset_cache;
+
+                presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+                filter_setup_dentry_ops(cache->cache_filter, 
+                                        dentry->d_op, 
+                                        &presto_dentry_ops);
+                dentry->d_op = filter_c2udops(cache->cache_filter);
+                /* if Lento does this, we won't have data */
+                if ( ISLENTO(presto_c2m(cache)) ) {
+                        presto_set(dentry, PRESTO_ATTR);
+                } else {
+                        presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+                }
+        }
+
+        error = presto_settime(fset, dir, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+        error = presto_settime(fset, dentry, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_MKDIR | 0x10);
+        presto_getversion(&new_dir_ver, dentry->d_inode);
+        if ( presto_do_kml(info, dentry->d_inode) )
+                error = presto_journal_mkdir(&rec, fset, dentry, &tgt_dir_ver,
+                                             &new_dir_ver, 
+					     dentry->d_inode->i_mode);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_MKDIR | 0x20);
+        if ( presto_do_expect(info, dentry->d_inode) )
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_MKDIR | 0x30);
+        EXIT;
+exit:
+        unlock_kernel();
+        presto_trans_commit(fset, handle);
+ exit_lock:
+	presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096); 
+        up(&dir->d_inode->i_zombie);
+        return error;
+}
+
+/*
+ * Look out: this function may change a normal dentry
+ * into a directory dentry (different size)..
+ */
+int lento_mkdir(const char *name, int mode, struct lento_vfs_context *info)
+{
+        int error;
+        char *pathname;
+        struct dentry *dentry;
+        struct presto_file_set *fset;
+        struct nameidata nd;
+
+        ENTRY;
+        CDEBUG(D_PIOCTL, "name: %s, mode %o, offset %d, recno %d, flags %x\n",
+               name, mode, info->slot_offset, info->recno, info->flags);
+        pathname = getname(name);
+        error = PTR_ERR(pathname);
+        if (IS_ERR(pathname)) {
+                EXIT;
+                return error;
+        }
+
+        if (path_init(pathname, LOOKUP_PARENT, &nd))
+                error = path_walk(pathname, &nd);
+        if (error)
+                goto out_name;
+
+        dentry = lookup_create(&nd, 1);
+        error = PTR_ERR(dentry);
+        if (!IS_ERR(dentry)) {
+                fset = presto_fset(dentry);
+                error = -EINVAL;
+                if ( !fset ) {
+                        printk("No fileset!\n");
+                        EXIT;
+                        goto out_dput;
+                }
+
+                error = presto_do_mkdir(fset, nd.dentry, dentry, 
+                                        mode & S_IALLUGO, info);
+out_dput:
+		dput(dentry);
+        }
+	up(&nd.dentry->d_inode->i_sem);
+	path_release(&nd);
+out_name:
+        EXIT;
+        putname(pathname);
+	CDEBUG(D_PIOCTL, "error: %d\n", error);
+        return error;
+}
+
+static void d_unhash(struct dentry *dentry)
+{
+        dget(dentry);
+        switch (atomic_read(&dentry->d_count)) {
+        default:
+                shrink_dcache_parent(dentry);
+                if (atomic_read(&dentry->d_count) != 2)
+                        break;
+        case 2:
+                d_drop(dentry);
+        }
+}
+
+int presto_do_rmdir(struct presto_file_set *fset, struct dentry *dir,
+                    struct dentry *dentry, struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error;
+        struct presto_version tgt_dir_ver, old_dir_ver;
+        struct inode_operations *iops;
+        void *handle;
+        int do_kml, do_expect;
+	int size;
+
+        ENTRY;
+        error = may_delete(dir->d_inode, dentry, 1);
+        if (error)
+                return error;
+
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+        if (!iops->rmdir) {
+                EXIT;
+                return error;
+        }
+
+	size = PRESTO_REQHIGH - dentry->d_inode->i_size; 
+	error = presto_reserve_space(fset->fset_cache, size); 
+	if (error) { 
+		EXIT;
+		return error;
+	}
+
+        presto_getversion(&tgt_dir_ver, dir->d_inode);
+        presto_getversion(&old_dir_ver, dentry->d_inode);
+        handle = presto_trans_start(fset, dir->d_inode, PRESTO_OP_RMDIR);
+        if ( IS_ERR(handle) ) {
+		presto_release_space(fset->fset_cache, size); 
+                printk("ERROR: presto_do_rmdir: no space for transaction. Tell Peter.\n");
+                return -ENOSPC;
+        }
+
+        DQUOT_INIT(dir->d_inode);
+
+        do_kml = presto_do_kml(info, dir->d_inode);
+        do_expect = presto_do_expect(info, dir->d_inode);
+
+        double_down(&dir->d_inode->i_zombie, &dentry->d_inode->i_zombie);
+        d_unhash(dentry);
+        if (IS_DEADDIR(dir->d_inode))
+                error = -ENOENT;
+        else if (d_mountpoint(dentry))
+                error = -EBUSY;
+        else {
+                lock_kernel();
+                error = iops->rmdir(dir->d_inode, dentry);
+                unlock_kernel();
+                if (!error) {
+                        dentry->d_inode->i_flags |= S_DEAD;
+			error = presto_settime(fset, dir, info, 
+					       ATTR_CTIME | ATTR_MTIME);
+		}
+        }
+        double_up(&dir->d_inode->i_zombie, &dentry->d_inode->i_zombie);
+        if (!error)
+                d_delete(dentry);
+        dput(dentry);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_RMDIR | 0x10);
+        if ( !error && do_kml )
+                error = presto_journal_rmdir(&rec, fset, dir, &tgt_dir_ver,
+                                             &old_dir_ver,
+                                             dentry->d_name.len,
+                                             dentry->d_name.name);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_RMDIR | 0x20);
+        if ( !error && do_expect ) 
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_RMDIR | 0x30);
+        EXIT;
+
+        presto_trans_commit(fset, handle);
+	presto_release_space(fset->fset_cache, size); 
+        return error;
+}
+
+int lento_rmdir(const char *pathname, struct lento_vfs_context *info)
+{
+        int error = 0;
+        char * name;
+        struct dentry *dentry;
+        struct presto_file_set *fset;
+        struct nameidata nd;
+
+        ENTRY;
+        name = getname(pathname);
+        if(IS_ERR(name))
+                return PTR_ERR(name);
+
+        if (path_init(name, LOOKUP_PARENT, &nd))
+                error = path_walk(name, &nd);
+        if (error)
+                goto exit;
+
+        switch(nd.last_type) {
+                case LAST_DOTDOT:
+                        error = -ENOTEMPTY;
+                        goto exit1;
+                case LAST_ROOT: case LAST_DOT:
+                        error = -EBUSY;
+                        goto exit1;
+        }
+        down(&nd.dentry->d_inode->i_sem);
+        dentry = lookup_hash(&nd.last, nd.dentry);
+        error = PTR_ERR(dentry);
+        if (!IS_ERR(dentry)) {
+                fset = presto_fset(dentry);
+                error = -EINVAL;
+                if ( !fset ) {
+                        printk("No fileset!\n");
+                        EXIT;
+                        goto exit_put;
+                }
+                error = presto_do_rmdir(fset, nd.dentry, dentry, info);
+        exit_put:
+                dput(dentry);
+        }
+        up(&nd.dentry->d_inode->i_sem);
+exit1:
+        EXIT;
+        path_release(&nd);
+exit:
+        EXIT;
+        putname(name);
+        return error;
+}
+
+int presto_do_mknod(struct presto_file_set *fset, struct dentry *dir,
+                    struct dentry *dentry, int mode, dev_t dev,
+                    struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error = -EPERM;
+        struct presto_version tgt_dir_ver, new_node_ver;
+        struct inode_operations *iops;
+        void *handle;
+
+        ENTRY;
+
+        down(&dir->d_inode->i_zombie);
+	/* one KML entry */ 
+	error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH); 
+	if (error) {
+		EXIT;
+        	up(&dir->d_inode->i_zombie);
+		return error;
+	}
+
+        if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        error = may_create(dir->d_inode, dentry);
+        if (error) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+        if (!iops->mknod) {
+                EXIT;
+                goto exit_lock;
+        }
+
+        DQUOT_INIT(dir->d_inode);
+        lock_kernel();
+        
+        error = -ENOSPC;
+        presto_getversion(&tgt_dir_ver, dir->d_inode);
+        handle = presto_trans_start(fset, dir->d_inode, PRESTO_OP_MKNOD);
+        if ( IS_ERR(handle) ) {
+		presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+                printk("presto_do_mknod: no space for transaction\n");
+                goto exit_lock2;
+        }
+
+        error = iops->mknod(dir->d_inode, dentry, mode, dev);
+        if ( dentry->d_inode &&
+             dentry->d_inode->i_gid != presto_excluded_gid) {
+                struct presto_cache *cache = fset->fset_cache;
+
+                presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+                filter_setup_dentry_ops(cache->cache_filter, dentry->d_op, 
+                                        &presto_dentry_ops);
+                dentry->d_op = filter_c2udops(cache->cache_filter);
+
+                /* if Lento does this, we won't have data */
+                if ( ISLENTO(presto_c2m(cache)) ) {
+                        presto_set(dentry, PRESTO_ATTR);
+                } else {
+                        presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+                }
+        }
+
+        error = presto_settime(fset, dir, info, ATTR_MTIME);
+        if (error) { 
+                EXIT;
+        }
+        error = presto_settime(fset, dentry, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+        }
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_MKNOD | 0x10);
+        presto_getversion(&new_node_ver, dentry->d_inode);
+        if ( presto_do_kml(info, dentry->d_inode) )
+                error = presto_journal_mknod(&rec, fset, dentry, &tgt_dir_ver,
+                                             &new_node_ver, 
+					     dentry->d_inode->i_mode,
+                                             MAJOR(dev), MINOR(dev) );
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_MKNOD | 0x20);
+        if ( presto_do_expect(info, dentry->d_inode) ) 
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_MKNOD | 0x30);
+        EXIT;
+        presto_trans_commit(fset, handle);
+ exit_lock2:
+        unlock_kernel();
+ exit_lock:
+	presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+        up(&dir->d_inode->i_zombie);
+        return error;
+}
+
+int lento_mknod(const char *filename, int mode, dev_t dev,
+                struct lento_vfs_context *info)
+{
+        int error = 0;
+        char * tmp;
+        struct dentry * dentry;
+        struct nameidata nd;
+        struct presto_file_set *fset;
+
+        ENTRY;
+
+        if (S_ISDIR(mode))
+                return -EPERM;
+        tmp = getname(filename);
+        if (IS_ERR(tmp))
+                return PTR_ERR(tmp);
+
+        if (path_init(tmp, LOOKUP_PARENT, &nd))
+                error = path_walk(tmp, &nd);
+        if (error)
+                goto out;
+        dentry = lookup_create(&nd, 0);
+        error = PTR_ERR(dentry);
+        if (!IS_ERR(dentry)) {
+                fset = presto_fset(dentry);
+                error = -EINVAL;
+                if ( !fset ) {
+                        printk("No fileset!\n");
+                        EXIT;
+                        goto exit_put;
+                }
+                switch (mode & S_IFMT) {
+                case 0: case S_IFREG:
+                        error = -EOPNOTSUPP;
+                        break;
+                case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
+                        error = presto_do_mknod(fset, nd.dentry, dentry, 
+                                                mode, dev, info);
+                        break;
+                case S_IFDIR:
+                        error = -EPERM;
+                        break;
+                default:
+                        error = -EINVAL;
+                }
+        exit_put:
+                dput(dentry);
+        }
+        up(&nd.dentry->d_inode->i_sem);
+        path_release(&nd);
+out:
+        putname(tmp);
+
+        return error;
+}
+
+static int do_rename(struct presto_file_set *fset,
+                     struct dentry *old_parent, struct dentry *old_dentry,
+                     struct dentry *new_parent, struct dentry *new_dentry,
+                     struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error;
+        struct inode_operations *iops;
+        struct presto_version src_dir_ver, tgt_dir_ver;
+        void *handle;
+	int new_inode_unlink = 0;
+        struct inode *old_dir = old_parent->d_inode;
+        struct inode *new_dir = new_parent->d_inode;
+
+        ENTRY;
+        presto_getversion(&src_dir_ver, old_dir);
+        presto_getversion(&tgt_dir_ver, new_dir);
+
+        error = -EPERM;
+        iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+        if (!iops || !iops->rename) {
+                EXIT;
+                return error;
+        }
+
+	error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH); 
+	if (error) {
+		EXIT;
+		return error;
+	}
+        handle = presto_trans_start(fset, old_dir, PRESTO_OP_RENAME);
+        if ( IS_ERR(handle) ) {
+		presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+                printk("presto_do_rename: no space for transaction\n");
+                return -ENOSPC;
+        }
+        if (new_dentry->d_inode && new_dentry->d_inode->i_nlink > 1) { 
+                dget(new_dentry); 
+                new_inode_unlink = 1;
+        }
+
+        error = iops->rename(old_dir, old_dentry, new_dir, new_dentry);
+
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+
+        if (new_inode_unlink) { 
+                error = presto_settime(fset, old_dentry, info, ATTR_CTIME);
+                dput(old_dentry); 
+                if (error) { 
+                        EXIT;
+                        goto exit;
+                }
+        }
+        error = presto_settime(fset, old_parent, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+        error = presto_settime(fset, new_parent, info, ATTR_CTIME | ATTR_MTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+
+        /* XXX make a distinction between cross file set
+         * and intra file set renames here
+         */
+        presto_debug_fail_blkdev(fset, PRESTO_OP_RENAME | 0x10);
+        if ( presto_do_kml(info, old_dir) )
+                error = presto_journal_rename(&rec, fset, old_dentry, new_dentry,
+                                              &src_dir_ver, &tgt_dir_ver);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_RENAME | 0x20);
+
+        if ( presto_do_expect(info, new_dir) ) 
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_RENAME | 0x30);
+        EXIT;
+exit:
+        presto_trans_commit(fset, handle);
+	presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+        return error;
+}
+
+static
+int presto_rename_dir(struct presto_file_set *fset, struct dentry *old_parent,
+                      struct dentry *old_dentry, struct dentry *new_parent,
+                      struct dentry *new_dentry, struct lento_vfs_context *info)
+{
+        int error;
+        struct inode *target;
+        struct inode *old_dir = old_parent->d_inode;
+        struct inode *new_dir = new_parent->d_inode;
+
+        if (old_dentry->d_inode == new_dentry->d_inode)
+                return 0;
+
+        error = may_delete(old_dir, old_dentry, 1);
+        if (error)
+                return error;
+
+        if (new_dir->i_dev != old_dir->i_dev)
+                return -EXDEV;
+
+        if (!new_dentry->d_inode)
+                error = may_create(new_dir, new_dentry);
+        else
+                error = may_delete(new_dir, new_dentry, 1);
+        if (error)
+                return error;
+
+        if (!old_dir->i_op || !old_dir->i_op->rename)
+                return -EPERM;
+
+        /*
+         * If we are going to change the parent - check write permissions,
+         * we'll need to flip '..'.
+         */
+        if (new_dir != old_dir) {
+                error = permission(old_dentry->d_inode, MAY_WRITE);
+        }
+        if (error)
+                return error;
+
+        DQUOT_INIT(old_dir);
+        DQUOT_INIT(new_dir);
+        down(&old_dir->i_sb->s_vfs_rename_sem);
+        error = -EINVAL;
+        if (is_subdir(new_dentry, old_dentry))
+                goto out_unlock;
+        target = new_dentry->d_inode;
+        if (target) { /* Hastur! Hastur! Hastur! */
+                triple_down(&old_dir->i_zombie,
+                            &new_dir->i_zombie,
+                            &target->i_zombie);
+                d_unhash(new_dentry);
+        } else
+                double_down(&old_dir->i_zombie,
+                            &new_dir->i_zombie);
+        if (IS_DEADDIR(old_dir)||IS_DEADDIR(new_dir))
+                error = -ENOENT;
+        else if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+                error = -EBUSY;
+        else 
+                error = do_rename(fset, old_parent, old_dentry,
+                                         new_parent, new_dentry, info);
+        if (target) {
+                if (!error)
+                        target->i_flags |= S_DEAD;
+                triple_up(&old_dir->i_zombie,
+                          &new_dir->i_zombie,
+                          &target->i_zombie);
+                if (d_unhashed(new_dentry))
+                        d_rehash(new_dentry);
+                dput(new_dentry);
+        } else
+                double_up(&old_dir->i_zombie,
+                          &new_dir->i_zombie);
+                
+        if (!error)
+                d_move(old_dentry,new_dentry);
+out_unlock:
+        up(&old_dir->i_sb->s_vfs_rename_sem);
+        return error;
+}
+
+static
+int presto_rename_other(struct presto_file_set *fset, struct dentry *old_parent,
+                        struct dentry *old_dentry, struct dentry *new_parent,
+                        struct dentry *new_dentry, struct lento_vfs_context *info)
+{
+        struct inode *old_dir = old_parent->d_inode;
+        struct inode *new_dir = new_parent->d_inode;
+        int error;
+
+        if (old_dentry->d_inode == new_dentry->d_inode)
+                return 0;
+
+        error = may_delete(old_dir, old_dentry, 0);
+        if (error)
+                return error;
+
+        if (new_dir->i_dev != old_dir->i_dev)
+                return -EXDEV;
+
+        if (!new_dentry->d_inode)
+                error = may_create(new_dir, new_dentry);
+        else
+                error = may_delete(new_dir, new_dentry, 0);
+        if (error)
+                return error;
+
+        if (!old_dir->i_op || !old_dir->i_op->rename)
+                return -EPERM;
+
+        DQUOT_INIT(old_dir);
+        DQUOT_INIT(new_dir);
+        double_down(&old_dir->i_zombie, &new_dir->i_zombie);
+        if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+                error = -EBUSY;
+        else
+                error = do_rename(fset, old_parent, old_dentry,
+                                         new_parent, new_dentry, info);
+        double_up(&old_dir->i_zombie, &new_dir->i_zombie);
+        if (error)
+                return error;
+        /* The following d_move() should become unconditional */
+        if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) {
+                d_move(old_dentry, new_dentry);
+        }
+        return 0;
+}
+
+int presto_do_rename(struct presto_file_set *fset, 
+              struct dentry *old_parent, struct dentry *old_dentry,
+              struct dentry *new_parent, struct dentry *new_dentry,
+              struct lento_vfs_context *info)
+{
+        if (S_ISDIR(old_dentry->d_inode->i_mode))
+                return presto_rename_dir(fset, old_parent,old_dentry,new_parent,
+                                      new_dentry, info);
+        else
+                return presto_rename_other(fset, old_parent, old_dentry,
+                                           new_parent,new_dentry, info);
+}
+
+
+int lento_do_rename(const char *oldname, const char *newname,
+                 struct lento_vfs_context *info)
+{
+        int error = 0;
+        struct dentry * old_dir, * new_dir;
+        struct dentry * old_dentry, *new_dentry;
+        struct nameidata oldnd, newnd;
+        struct presto_file_set *fset;
+
+        ENTRY;
+
+        if (path_init(oldname, LOOKUP_PARENT, &oldnd))
+                error = path_walk(oldname, &oldnd);
+
+        if (error)
+                goto exit;
+
+        if (path_init(newname, LOOKUP_PARENT, &newnd))
+                error = path_walk(newname, &newnd);
+        if (error)
+                goto exit1;
+
+        error = -EXDEV;
+        if (oldnd.mnt != newnd.mnt)
+                goto exit2;
+
+        old_dir = oldnd.dentry;
+        error = -EBUSY;
+        if (oldnd.last_type != LAST_NORM)
+                goto exit2;
+
+        new_dir = newnd.dentry;
+        if (newnd.last_type != LAST_NORM)
+                goto exit2;
+
+        double_lock(new_dir, old_dir);
+
+        old_dentry = lookup_hash(&oldnd.last, old_dir);
+        error = PTR_ERR(old_dentry);
+        if (IS_ERR(old_dentry))
+                goto exit3;
+        /* source must exist */
+        error = -ENOENT;
+        if (!old_dentry->d_inode)
+                goto exit4;
+        fset = presto_fset(old_dentry);
+        error = -EINVAL;
+        if ( !fset ) {
+                printk("No fileset!\n");
+                EXIT;
+                goto exit4;
+        }
+        /* unless the source is a directory trailing slashes give -ENOTDIR */
+        if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+                error = -ENOTDIR;
+                if (oldnd.last.name[oldnd.last.len])
+                        goto exit4;
+                if (newnd.last.name[newnd.last.len])
+                        goto exit4;
+        }
+        new_dentry = lookup_hash(&newnd.last, new_dir);
+        error = PTR_ERR(new_dentry);
+        if (IS_ERR(new_dentry))
+                goto exit4;
+
+        lock_kernel();
+        error = presto_do_rename(fset, old_dir, old_dentry,
+                                   new_dir, new_dentry, info);
+        unlock_kernel();
+
+        dput(new_dentry);
+exit4:
+        dput(old_dentry);
+exit3:
+        double_up(&new_dir->d_inode->i_sem, &old_dir->d_inode->i_sem);
+exit2:
+        path_release(&newnd);
+exit1:
+        path_release(&oldnd);
+exit:
+        return error;
+}
+
+int  lento_rename(const char * oldname, const char * newname,
+                  struct lento_vfs_context *info)
+{
+        int error;
+        char * from;
+        char * to;
+
+        from = getname(oldname);
+        if(IS_ERR(from))
+                return PTR_ERR(from);
+        to = getname(newname);
+        error = PTR_ERR(to);
+        if (!IS_ERR(to)) {
+                error = lento_do_rename(from,to, info);
+                putname(to);
+        }
+        putname(from);
+        return error;
+}
+
+struct dentry *presto_iopen(struct dentry *dentry,
+                            ino_t ino, unsigned int generation)
+{
+        struct presto_file_set *fset;
+        char name[48];
+        int error;
+
+        ENTRY;
+        /* see if we already have the dentry we want */
+        if (dentry->d_inode && dentry->d_inode->i_ino == ino &&
+            dentry->d_inode->i_generation == generation) {
+                EXIT;
+                return dentry;
+        }
+
+        /* Make sure we have a cache beneath us.  We should always find at
+         * least one dentry inside the cache (if it exists), otherwise not
+         * even the cache root exists, or we passed in a bad name.
+         */
+        fset = presto_fset(dentry);
+        error = -EINVAL;
+        if (!fset) {
+                printk("No fileset for %*s!\n",
+                       dentry->d_name.len, dentry->d_name.name);
+                EXIT;
+                dput(dentry);
+                return ERR_PTR(error);
+        }
+        dput(dentry);
+
+        sprintf(name, "%s%#lx%c%#x",
+                PRESTO_ILOOKUP_MAGIC, ino, PRESTO_ILOOKUP_SEP, generation);
+        CDEBUG(D_PIOCTL, "opening %ld by number (as %s)\n", ino, name);
+        return lookup_one_len(name, fset->fset_mtpt, strlen(name));
+}
+
+static struct file *presto_filp_dopen(struct dentry *dentry, int flags)
+{
+        struct file *f;
+        struct inode *inode;
+        int flag, error;
+
+        ENTRY;
+        error = -ENFILE;
+        f = get_empty_filp();
+        if (!f) {
+                CDEBUG(D_PIOCTL, "error getting file pointer\n");
+                EXIT;
+                goto out;
+        }
+        f->f_flags = flag = flags;
+        f->f_mode = (flag+1) & O_ACCMODE;
+        inode = dentry->d_inode;
+        if (f->f_mode & FMODE_WRITE) {
+                error = get_write_access(inode);
+                if (error) {
+                        CDEBUG(D_PIOCTL, "error getting write access\n");
+                        EXIT;
+                        goto cleanup_file;
+                }
+        }
+
+        f->f_dentry = dentry;
+        f->f_pos = 0;
+        f->f_reada = 0;
+        f->f_op = NULL;
+        if (inode->i_op)
+                /* XXX should we set to presto ops, or leave at cache ops? */
+                f->f_op = inode->i_fop;
+        if (f->f_op && f->f_op->open) {
+                error = f->f_op->open(inode, f);
+                if (error) {
+                        CDEBUG(D_PIOCTL, "error calling cache 'open'\n");
+                        EXIT;
+                        goto cleanup_all;
+                }
+        }
+        f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+
+        return f;
+
+cleanup_all:
+        if (f->f_mode & FMODE_WRITE)
+                put_write_access(inode);
+cleanup_file:
+        put_filp(f);
+out:
+        return ERR_PTR(error);
+}
+
+
+/* Open an inode by number.  We pass in the cache root name (or a subdirectory
+ * from the cache that is guaranteed to exist) to be able to access the cache.
+ */
+int lento_iopen(const char *name, ino_t ino, unsigned int generation,
+                int flags)
+{
+        char * tmp;
+        struct dentry *dentry;
+        struct nameidata nd;
+        int fd;
+        int error;
+
+        ENTRY;
+        CDEBUG(D_PIOCTL,
+               "open %s:inode %#lx (%ld), generation %x (%d), flags %d \n",
+               name, ino, ino, generation, generation, flags);
+        /* We don't allow creation of files by number only, as it would
+         * lead to a dangling files not in any directory.  We could also
+         * just turn off the flag and ignore it.
+         */
+        if (flags & O_CREAT) {
+                printk(KERN_WARNING __FUNCTION__
+                       ": create file by inode number (%ld) not allowed\n",ino);
+                EXIT;
+                return -EACCES;
+        }
+
+        tmp = getname(name);
+        if (IS_ERR(tmp)) {
+                EXIT;
+                return PTR_ERR(tmp);
+        }
+
+        lock_kernel();
+again:  /* look the named file or a parent directory so we can get the cache */
+        error = presto_walk(tmp, &nd);
+        if ( error && error != -ENOENT ) {
+                EXIT;
+                return error;
+        } 
+        if (error == -ENOENT)
+                dentry = NULL;
+        else 
+                dentry = nd.dentry;
+
+        /* we didn't find the named file, so see if a parent exists */
+        if (!dentry) {
+                char *slash;
+
+                slash = strrchr(tmp, '/');
+                if (slash && slash != tmp) {
+                        *slash = '\0';
+			path_release(&nd);
+                        goto again;
+                }
+                /* we should never get here... */
+                CDEBUG(D_PIOCTL, "no more path components to try!\n");
+                fd = -ENOENT;
+                goto exit;
+        }
+        CDEBUG(D_PIOCTL, "returned dentry %p\n", dentry);
+
+        dentry = presto_iopen(dentry, ino, generation);
+        fd = PTR_ERR(dentry);
+        if (IS_ERR(dentry)) {
+                EXIT;
+                goto exit;
+        }
+
+        /* XXX start of code that might be replaced by something like:
+         * if (flags & (O_WRONLY | O_RDWR)) {
+         *      error = get_write_access(dentry->d_inode);
+         *      if (error) {
+         *              EXIT;
+         *              goto cleanup_dput;
+         *      }
+         * }
+         * fd = open_dentry(dentry, flags);
+         *
+         * including the presto_filp_dopen() function (check dget counts!)
+         */
+        fd = get_unused_fd();
+        if (fd < 0) {
+                EXIT;
+                goto cleanup_dput;
+        }
+
+        {
+                int error;
+                struct file * f = presto_filp_dopen(dentry, flags);
+                error = PTR_ERR(f);
+                if (IS_ERR(f)) {
+                        put_unused_fd(fd);
+                        fd = error;
+                        EXIT;
+                        goto cleanup_dput;
+                }
+                fd_install(fd, f);
+        }
+        /* end of code that might be replaced by open_dentry */
+
+        EXIT;
+exit:
+        unlock_kernel();
+	path_release(&nd);
+        putname(tmp);
+        return fd;
+
+cleanup_dput:
+        putname(&nd);
+        goto exit;
+}
+
+int lento_close(unsigned int fd, struct lento_vfs_context *info)
+{
+        struct rec_info rec;
+        int error;
+        struct file * filp;
+        struct dentry *dentry;
+        int do_kml, do_expect;
+
+        ENTRY;
+        lock_kernel();
+
+        error = -EBADF;
+        filp = fcheck(fd);
+        if (filp) {
+
+                struct files_struct * files = current->files;
+                dentry = filp->f_dentry;
+                dget(dentry);
+                do_kml = presto_do_kml(info, dentry->d_inode);
+                do_expect = presto_do_expect(info, dentry->d_inode);
+                files->fd[fd] = NULL;
+                put_unused_fd(fd);
+                FD_CLR(fd, files->close_on_exec);
+                error = filp_close(filp, files);
+        } else {
+                EXIT;
+                return error;
+        }
+
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+
+        if ( do_kml ) { 
+                struct presto_file_set *fset;
+                struct presto_version new_file_ver;
+
+                fset = presto_fset(dentry);
+                error = -EINVAL;
+                if (!fset) {
+                        printk("No fileset for %*s!\n",
+                               dentry->d_name.len, dentry->d_name.name);
+                        EXIT;
+                        goto exit;
+                }
+                presto_getversion(&new_file_ver, dentry->d_inode);
+                error = presto_journal_close(&rec, fset, filp, dentry, 
+					     &new_file_ver);
+                if ( error ) {
+                        printk("presto: close error %d!\n", error);
+                        EXIT;
+                        goto exit;
+                }
+                if ( do_expect ) 
+
+                        error = presto_write_last_rcvd(&rec, fset, info);
+        }
+
+        EXIT;
+exit:
+        dput(dentry);
+        unlock_kernel();
+        return error;
+}
+
+#ifdef CONFIG_FS_EXT_ATTR
+
+#ifdef CONFIG_FS_POSIX_ACL
+/* Posix ACL code changes i_mode without using a notify_change (or
+ * a mark_inode_dirty!). We need to duplicate this at the reintegrator
+ * which is done by this function. This function also takes care of 
+ * resetting the cached posix acls in this inode. If we don't reset these
+ * VFS continues using the old acl information, which by now may be out of
+ * date.
+ */
+int presto_setmode(struct presto_file_set *fset, struct dentry *dentry,
+                   mode_t mode)
+{
+        struct inode *inode = dentry->d_inode;
+
+        ENTRY;
+        /* The extended attributes for this inode were modified. 
+         * At this point we can not be sure if any of the ACL 
+         * information for this inode was updated. So we will 
+         * force VFS to reread the acls. Note that we do this 
+         * only when called from the SETEXTATTR ioctl, which is why we
+         * do this while setting the mode of the file. Also note
+         * that mark_inode_dirty is not be needed for i_*acl only
+         * to force i_mode info to disk, and should be removed once
+         * we use notify_change to update the mode.
+         * XXX: is mode setting really needed? Just setting acl's should
+         * be enough! VFS should change the i_mode as needed? SHP
+         */
+        if (inode->i_acl && 
+            inode->i_acl != POSIX_ACL_NOT_CACHED) 
+            posix_acl_release(inode->i_acl);
+        if (inode->i_default_acl && 
+            inode->i_default_acl != POSIX_ACL_NOT_CACHED) 
+            posix_acl_release(inode->i_default_acl);
+        inode->i_acl = POSIX_ACL_NOT_CACHED;
+        inode->i_default_acl = POSIX_ACL_NOT_CACHED;
+        inode->i_mode = mode;
+        /* inode should already be dirty...but just in case */
+        mark_inode_dirty(inode);
+        return 0;
+
+#if 0
+        /* XXX: The following code is the preferred way to set mode, 
+         * however, I need to carefully go through possible recursion
+         * paths back into presto. See comments in presto_do_setattr.
+         */
+        {    
+        int error=0; 
+        struct super_operations *sops;
+        struct iattr iattr;
+
+        iattr.ia_mode = mode;
+        iattr.ia_valid = ATTR_MODE|ATTR_FORCE;
+
+        error = -EPERM;
+        sops = filter_c2csops(fset->fset_cache->cache_filter); 
+        if (!sops &&
+            !sops->notify_change) {
+                EXIT;
+                return error;
+        }
+
+        error = sops->notify_change(dentry, &iattr);
+
+        EXIT;
+        return error;
+        }
+#endif
+}
+#endif
+
+/* setextattr Interface to cache filesystem */
+int presto_do_set_ext_attr(struct presto_file_set *fset, 
+                           struct dentry *dentry, 
+                           const char *name, void *buffer,
+                           size_t buffer_len, int flags, mode_t *mode,
+                           struct lento_vfs_context *info) 
+{
+        struct rec_info rec;
+        struct inode *inode = dentry->d_inode;
+        struct inode_operations *iops;
+        int error;
+        struct presto_version ver;
+        void *handle;
+        char temp[PRESTO_EXT_ATTR_NAME_MAX+1];
+
+        ENTRY;
+        error = -EROFS;
+        if (IS_RDONLY(inode)) {
+                EXIT;
+                return -EROFS;
+        }
+
+        if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+                EXIT;
+                return -EPERM;
+        }
+
+        presto_getversion(&ver, inode);
+        error = -EPERM;
+        /* We need to invoke different filters based on whether
+         * this dentry is a regular file, directory or symlink.
+         */
+        switch (inode->i_mode & S_IFMT) {
+                case S_IFLNK: /* symlink */
+                    iops = filter_c2csiops(fset->fset_cache->cache_filter); 
+                    break;
+                case S_IFDIR: /* directory */
+                    iops = filter_c2cdiops(fset->fset_cache->cache_filter); 
+                    break;
+                case S_IFREG:
+                default: /* everything else including regular files */
+                    iops = filter_c2cfiops(fset->fset_cache->cache_filter); 
+        }
+
+        if (!iops && !iops->set_ext_attr) {
+                EXIT;
+                return error;
+        }
+
+        error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH); 
+        if (error) {
+                EXIT;
+                return error;
+        }
+
+        
+        handle = presto_trans_start(fset,dentry->d_inode,PRESTO_OP_SETEXTATTR);
+        if ( IS_ERR(handle) ) {
+                printk("presto_do_set_ext_attr: no space for transaction\n");
+                presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+                return -ENOSPC;
+        }
+
+        /* We first "truncate" name to the maximum allowable in presto */
+        /* This simulates the strncpy_from_use code in fs/ext_attr.c */
+        strncpy(temp,name,sizeof(temp));
+
+        /* Pass down to cache*/
+        error = iops->set_ext_attr(inode,temp,buffer,buffer_len,flags);
+        if (error) {
+                EXIT;
+                goto exit;
+        }
+
+#ifdef CONFIG_FS_POSIX_ACL
+        /* Reset mode if specified*/
+        /* XXX: when we do native acl support, move this code out! */
+        if (mode != NULL) {
+                error = presto_setmode(fset, dentry, *mode);
+                if (error) { 
+                    EXIT;
+                    goto exit;
+                }
+        }
+#endif
+
+        /* Reset ctime. Only inode change time (ctime) is affected */
+        error = presto_settime(fset, dentry, info, ATTR_CTIME);
+        if (error) { 
+                EXIT;
+                goto exit;
+        }
+
+        if (flags & EXT_ATTR_FLAG_USER) {
+                printk(" USER flag passed to presto_do_set_ext_attr!\n");
+                *(int *)0 = 1;
+        }
+
+        /* We are here, so set_ext_attr succeeded. We no longer need to keep
+         * track of EXT_ATTR_FLAG_{EXISTS,CREATE}, instead, we will force
+         * the attribute value during log replay. -SHP
+         */
+        flags &= ~(EXT_ATTR_FLAG_EXISTS | EXT_ATTR_FLAG_CREATE);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SETEXTATTR | 0x10);
+        if ( presto_do_kml(info, dentry->d_inode) )
+                error = presto_journal_set_ext_attr
+                        (&rec, fset, dentry, &ver, name, buffer, 
+                         buffer_len, flags);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SETEXTATTR | 0x20);
+        if ( presto_do_expect(info, dentry->d_inode) )
+                error = presto_write_last_rcvd(&rec, fset, info);
+
+        presto_debug_fail_blkdev(fset, PRESTO_OP_SETEXTATTR | 0x30);
+        EXIT;
+exit:
+        presto_release_space(fset->fset_cache, PRESTO_REQHIGH); 
+        presto_trans_commit(fset, handle);
+
+        return error;
+}
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)