patch-2.4.15 linux/fs/intermezzo/dir.c
Next file: linux/fs/intermezzo/ext_attr.c
Previous file: linux/fs/intermezzo/dcache.c
Back to the patch index
Back to the overall index
- Lines: 894
- Date:
Tue Nov 13 09:20:56 2001
- Orig file:
v2.4.14/linux/fs/intermezzo/dir.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.14/linux/fs/intermezzo/dir.c linux/fs/intermezzo/dir.c
@@ -0,0 +1,893 @@
+/*
+ *
+ *
+ * Copyright (C) 2000 Stelias Computing, Inc.
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 Tacitus Systems
+ * Copyright (C) 2000 Peter J. Braam
+ *
+ */
+
+
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/smp_lock.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_upcall.h>
+#include <linux/intermezzo_psdev.h>
+
+static inline void presto_relock_sem(struct inode *dir)
+{
+ /* the lock from sys_mkdir / lookup_create */
+ down(&dir->i_sem);
+ /* the rest is done by the do_{create,mkdir, ...} */
+}
+
+static inline void presto_relock_other(struct inode *dir)
+{
+ /* vfs_mkdir locks */
+ down(&dir->i_zombie);
+ lock_kernel();
+}
+
+static inline void presto_fulllock(struct inode *dir)
+{
+ /* the lock from sys_mkdir / lookup_create */
+ down(&dir->i_sem);
+ /* vfs_mkdir locks */
+ down(&dir->i_zombie);
+ lock_kernel();
+}
+
+static inline void presto_unlock(struct inode *dir)
+{
+ /* vfs_mkdir locks */
+ unlock_kernel();
+ up(&dir->i_zombie);
+ /* the lock from sys_mkdir / lookup_create */
+ up(&dir->i_sem);
+}
+
+
+/*
+ * these are initialized in super.c
+ */
+extern int presto_permission(struct inode *inode, int mask);
+int presto_ilookup_uid = 0;
+
+extern int presto_prep(struct dentry *, struct presto_cache **,
+ struct presto_file_set **);
+
+static int dentry2id(struct dentry *dentry, ino_t *id, unsigned int *generation)
+{
+ char *tmpname;
+ char *next;
+ int error = 0;
+
+ ENTRY;
+ if (dentry->d_name.len > EXT2_NAME_LEN) {
+ EXIT;
+ return -ENAMETOOLONG;
+ }
+
+ /* prefix is 7 characters: '...ino:' */
+ if ( dentry->d_name.len < 7 ||
+ memcmp(dentry->d_name.name, PRESTO_ILOOKUP_MAGIC, 7) != 0 ) {
+ EXIT;
+ return 1;
+ }
+
+ PRESTO_ALLOC(tmpname, char *, dentry->d_name.len - 7 + 1);
+ if ( !tmpname ) {
+ EXIT;
+ return -ENOMEM;
+ }
+
+ memcpy(tmpname, dentry->d_name.name + 7, dentry->d_name.len - 7);
+ *(tmpname + dentry->d_name.len) = '\0';
+
+ /* name is of the form <inode number>:<generation> */
+ *id = simple_strtoul(tmpname, &next, 0);
+ if ( *next == PRESTO_ILOOKUP_SEP ) {
+ *generation = simple_strtoul(next + 1, 0, 0);
+ CDEBUG(D_INODE, "INO to find = %s\n", tmpname);
+ CDEBUG(D_INODE, "Id = %lx (%lu), generation %x (%d)\n",
+ *id, *id, *generation, *generation);
+ } else
+ error = 1;
+
+ PRESTO_FREE(tmpname, dentry->d_name.len - 7 + 1);
+ EXIT;
+ return error;
+}
+
+static int presto_opendir_upcall(int minor, struct dentry *de,
+ struct dentry *root, int async)
+{
+ int rc;
+ char *path, *buffer;
+ int pathlen;
+
+ PRESTO_ALLOC(buffer, char *, PAGE_SIZE);
+ if ( !buffer ) {
+ printk("PRESTO: out of memory!\n");
+ return ENOMEM;
+ }
+ path = presto_path(de, root, buffer, PAGE_SIZE);
+ pathlen = MYPATHLEN(buffer, path);
+ CDEBUG(D_INODE, "path: %*s, len %d\n", pathlen, path, pathlen);
+ rc = lento_opendir(minor, pathlen, path, async);
+ PRESTO_FREE(buffer, PAGE_SIZE);
+ return rc;
+}
+
+inline int presto_can_ilookup(void)
+{
+ return (current->euid == presto_ilookup_uid ||
+ capable(CAP_DAC_READ_SEARCH));
+}
+
+struct dentry *presto_ilookup(struct inode *dir, struct dentry *dentry,
+ ino_t ino, unsigned int generation)
+{
+ struct inode *inode;
+ int error;
+
+ ENTRY;
+
+ /* if we can't ilookup, forbid anything with this name to
+ * avoid any security issues/name clashes.
+ */
+ if ( !presto_can_ilookup() ) {
+ CDEBUG(D_CACHE, "ilookup denied: euid %u, ilookup_uid %u\n",
+ current->euid, presto_ilookup_uid);
+ EXIT;
+ return ERR_PTR(-EPERM);
+ }
+ inode = iget(dir->i_sb, ino);
+ if (!inode || is_bad_inode(inode)) {
+ CDEBUG(D_PIOCTL, "fatal: invalid inode %ld (%s).\n",
+ ino, inode ? inode->i_nlink ? "bad inode" :
+ "no links" : "NULL");
+ error = -ENOENT;
+ EXIT;
+ goto cleanup_iput;
+ } else if (inode->i_nlink == 0) {
+ /* This is quite evil, but we have little choice. If we were
+ * to iput() again with i_nlink == 0, delete_inode would get
+ * called again, which ext3 really Does Not Like. */
+ atomic_dec(&inode->i_count);
+ EXIT;
+ return ERR_PTR(-ENOENT);
+ }
+
+ /* We need to make sure we have the right inode (by checking the
+ * generation) so we don't write into the wrong file (old inode was
+ * deleted and then a new one was created with the same number).
+ */
+ if (inode->i_generation != generation) {
+ CDEBUG(D_PIOCTL, "fatal: bad generation %u (want %u)\n",
+ inode->i_generation, generation);
+ error = -ENOENT;
+ EXIT;
+ goto cleanup_iput;
+ }
+
+ d_instantiate(dentry, inode);
+ dentry->d_flags |= DCACHE_NFSD_DISCONNECTED; /* NFS hack */
+
+ EXIT;
+ return NULL;
+
+cleanup_iput:
+ if (inode)
+ iput(inode);
+ return ERR_PTR(error);
+}
+
+
+struct dentry *presto_lookup(struct inode * dir, struct dentry *dentry)
+{
+ int rc = 0;
+ struct dentry *de;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ int error;
+ int minor;
+ ino_t ino;
+ unsigned int generation;
+
+ ENTRY;
+ CDEBUG(D_CACHE, "calling presto_prep on dentry %p\n", dentry);
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return ERR_PTR(error);
+ }
+ minor = presto_c2m(cache);
+
+ CDEBUG(D_CACHE, "dir ino: %ld, name: %*s\n",
+ dir->i_ino, dentry->d_name.len, dentry->d_name.name);
+ if ( ISLENTO(minor) )
+ CDEBUG(D_CACHE, "We are lento\n");
+
+ rc = dentry2id(dentry, &ino, &generation);
+ CDEBUG(D_CACHE, "dentry2id returned %d\n", rc);
+ if ( rc < 0 ) {
+ EXIT;
+ goto exit;
+ }
+
+ if ( rc == 0 ) {
+ de = presto_ilookup(dir, dentry, ino, generation);
+ } else {
+ struct inode_operations *iops = filter_c2cdiops(cache->cache_filter);
+ rc = 0;
+ /* recursively do a cache lookup in dir */
+ if (iops && iops->lookup)
+ de = iops->lookup(dir, dentry);
+ else {
+ printk("filesystem has no lookup\n");
+ EXIT;
+ goto exit;
+ }
+ }
+ /* XXX this needs some work to handle returning de if we get it */
+ filter_setup_dentry_ops(cache->cache_filter,
+ dentry->d_op, &presto_dentry_ops);
+ dentry->d_op = filter_c2udops(cache->cache_filter);
+ if ( IS_ERR(de) ) {
+ rc = PTR_ERR(de);
+ CDEBUG(D_CACHE, "dentry lookup error %d\n", rc);
+ EXIT;
+ goto exit;
+ }
+
+ presto_set_dd(dentry);
+
+ /* some file systems set the methods in lookup, not in
+ read_inode, as a result we should set the methods here
+ as well as in read_inode
+ */
+ if (dentry->d_inode) {
+ presto_set_ops(dentry->d_inode, cache->cache_filter);
+ }
+ EXIT;
+exit:
+ return ERR_PTR(rc);
+}
+
+int presto_setattr(struct dentry *de, struct iattr *iattr)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct lento_vfs_context info = { 0, 0, 0 };
+
+ ENTRY;
+ error = presto_prep(de, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ if (!iattr->ia_valid)
+ CDEBUG(D_INODE, "presto_setattr: iattr is not valid\n");
+
+ CDEBUG(D_INODE, "valid %#x, mode %#o, uid %u, gid %u, size %Lu, "
+ "atime %lu mtime %lu ctime %lu flags %d\n",
+ iattr->ia_valid, iattr->ia_mode, iattr->ia_uid, iattr->ia_gid,
+ iattr->ia_size, iattr->ia_atime, iattr->ia_mtime,
+ iattr->ia_ctime, iattr->ia_attr_flags);
+
+ if ( presto_get_permit(de->d_inode) < 0 ) {
+ EXIT;
+ return -EROFS;
+ }
+
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_setattr(fset, de, iattr, &info);
+ presto_put_permit(de->d_inode);
+ return error;
+}
+
+/*
+ * Now the meat: the fs operations that require journaling
+ *
+ *
+ * XXX: some of these need modifications for hierarchical filesets
+ */
+
+int presto_prep(struct dentry *dentry, struct presto_cache **cache,
+ struct presto_file_set **fset)
+{
+ *fset = presto_fset(dentry);
+ if ( !*fset ) {
+ CDEBUG(D_INODE, "No file set for dentry at %p\n", dentry);
+ return -EROFS;
+ }
+
+ *cache = (*fset)->fset_cache;
+ if ( !*cache ) {
+ printk("PRESTO: BAD, BAD: cannot find cache\n");
+ return -EBADF;
+ }
+
+ CDEBUG(D_PIOCTL, "---> cache flags %x, fset flags %x\n",
+ (*cache)->cache_flags, (*fset)->fset_flags);
+ if( presto_is_read_only(*fset) ) {
+ printk("PRESTO: cannot modify read-only fileset, minor %d.\n",
+ presto_c2m(*cache));
+ return -EROFS;
+ }
+ return 0;
+}
+
+static int presto_create(struct inode * dir, struct dentry * dentry, int mode)
+{
+ int error;
+ struct presto_cache *cache;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+ struct presto_file_set *fset;
+
+ ENTRY;
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+ presto_unlock(dir);
+
+ /* Does blocking and non-blocking behavious need to be
+ checked for. Without blocking (return 1), the permit
+ was acquired without reintegration
+ */
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_create(fset, parent, dentry, mode, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ EXIT;
+ return error;
+}
+
+static int presto_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int error;
+ struct presto_cache *cache, *new_cache;
+ struct presto_file_set *fset, *new_fset;
+ struct dentry *parent = new_dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_prep(old_dentry, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(new_dentry->d_parent, &new_cache, &new_fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ if (fset != new_fset) {
+ EXIT;
+ return -EXDEV;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(old_dentry->d_inode) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = new_dentry->d_parent;
+
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_link(fset, old_dentry, parent,
+ new_dentry, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ presto_put_permit(old_dentry->d_inode);
+ return error;
+}
+
+static int presto_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+ int error;
+ struct presto_file_set *fset;
+ struct presto_cache *cache;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ error = presto_do_mkdir(fset, parent, dentry, mode, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ return error;
+}
+
+
+static int presto_symlink(struct inode *dir, struct dentry *dentry,
+ const char *name)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_symlink(fset, parent, dentry, name, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ return error;
+}
+
+int presto_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_unlink(fset, parent, dentry, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ return error;
+}
+
+static int presto_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ CDEBUG(D_FILE, "prepping presto\n");
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ CDEBUG(D_FILE, "unlocking\n");
+ /* We need to dget() before the dput in double_unlock, to ensure we
+ * still have dentry references. double_lock doesn't do dget for us.
+ */
+ unlock_kernel();
+ if (d_unhashed(dentry))
+ d_rehash(dentry);
+ double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
+ double_up(&dir->i_sem, &dentry->d_inode->i_sem);
+
+ CDEBUG(D_FILE, "getting permit\n");
+ if ( presto_get_permit(parent->d_inode) < 0 ) {
+ EXIT;
+ double_down(&dir->i_sem, &dentry->d_inode->i_sem);
+ double_down(&dir->i_zombie, &dentry->d_inode->i_zombie);
+
+ lock_kernel();
+ return -EROFS;
+ }
+ CDEBUG(D_FILE, "locking\n");
+
+ double_down(&dir->i_sem, &dentry->d_inode->i_sem);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_rmdir(fset, parent, dentry, &info);
+ presto_put_permit(parent->d_inode);
+ lock_kernel();
+ EXIT;
+ return error;
+}
+
+static int presto_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_mknod(fset, parent, dentry, mode, rdev, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ EXIT;
+ return error;
+}
+
+inline void presto_triple_unlock(struct inode *old_dir, struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* rename_dir case */
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ if (triple) {
+ triple_up(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &new_dentry->d_inode->i_zombie);
+ } else {
+ double_up(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ }
+ up(&old_dir->i_sb->s_vfs_rename_sem);
+ } else /* this case is rename_other */
+ double_up(&old_dir->i_zombie, &new_dir->i_zombie);
+ /* done by do_rename */
+ unlock_kernel();
+ double_up(&old_dir->i_sem, &new_dir->i_sem);
+}
+
+inline void presto_triple_fulllock(struct inode *old_dir,
+ struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* done by do_rename */
+ double_down(&old_dir->i_sem, &new_dir->i_sem);
+ lock_kernel();
+ /* rename_dir case */
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ down(&old_dir->i_sb->s_vfs_rename_sem);
+ if (triple) {
+ triple_down(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &new_dentry->d_inode->i_zombie);
+ } else {
+ double_down(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ }
+ } else /* this case is rename_other */
+ double_down(&old_dir->i_zombie, &new_dir->i_zombie);
+}
+
+inline void presto_triple_relock_sem(struct inode *old_dir,
+ struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* done by do_rename */
+ double_down(&old_dir->i_sem, &new_dir->i_sem);
+ lock_kernel();
+}
+
+inline void presto_triple_relock_other(struct inode *old_dir,
+ struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* rename_dir case */
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ down(&old_dir->i_sb->s_vfs_rename_sem);
+ if (triple) {
+ triple_down(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &new_dentry->d_inode->i_zombie);
+ } else {
+ double_down(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ }
+ } else /* this case is rename_other */
+ double_down(&old_dir->i_zombie, &new_dir->i_zombie);
+}
+
+
+// XXX this can be optimized: renamtes across filesets only require
+// multiple KML records, but can locally be executed normally.
+int presto_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+ struct presto_cache *cache, *new_cache;
+ struct presto_file_set *fset, *new_fset;
+ struct lento_vfs_context info;
+ struct dentry *old_parent = old_dentry->d_parent;
+ struct dentry *new_parent = new_dentry->d_parent;
+ int triple;
+
+ ENTRY;
+ error = presto_prep(old_dentry, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+ error = presto_prep(new_parent, &new_cache, &new_fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ if ( fset != new_fset ) {
+ EXIT;
+ return -EXDEV;
+ }
+
+ /* We need to do dget before the dput in double_unlock, to ensure we
+ * still have dentry references. double_lock doesn't do dget for us.
+ */
+
+ triple = (S_ISDIR(old_dentry->d_inode->i_mode) && new_dentry->d_inode)?
+ 1:0;
+
+ presto_triple_unlock(old_dir, new_dir, old_dentry, new_dentry, triple);
+
+ if ( presto_get_permit(old_dir) < 0 ) {
+ EXIT;
+ presto_triple_fulllock(old_dir, new_dir, old_dentry, new_dentry, triple);
+ return -EROFS;
+ }
+ if ( presto_get_permit(new_dir) < 0 ) {
+ EXIT;
+ presto_triple_fulllock(old_dir, new_dir, old_dentry, new_dentry, triple);
+ return -EROFS;
+ }
+
+ presto_triple_relock_sem(old_dir, new_dir, old_dentry, new_dentry, triple);
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_rename(fset, old_parent, old_dentry, new_parent,
+ new_dentry, &info);
+ presto_triple_relock_other(old_dir, new_dir, old_dentry, new_dentry, triple);
+
+ presto_put_permit(new_dir);
+ presto_put_permit(old_dir);
+ return error;
+}
+
+/* basically this allows the ilookup processes access to all files for
+ * reading, while not making ilookup totally insecure. This could all
+ * go away if we could set the CAP_DAC_READ_SEARCH capability for the client.
+ */
+/* If posix acls are available, the underlying cache fs will export the
+ * appropriate permission function. Thus we do not worry here about ACLs
+ * or EAs. -SHP
+ */
+int presto_permission(struct inode *inode, int mask)
+{
+ unsigned short mode = inode->i_mode;
+ struct presto_cache *cache;
+ int rc;
+
+ ENTRY;
+ if ( presto_can_ilookup() && !(mask & S_IWOTH)) {
+ CDEBUG(D_CACHE, "ilookup on %ld OK\n", inode->i_ino);
+ EXIT;
+ return 0;
+ }
+
+ cache = presto_get_cache(inode);
+
+ if ( cache ) {
+ /* we only override the file/dir permission operations */
+ struct inode_operations *fiops = filter_c2cfiops(cache->cache_filter);
+ struct inode_operations *diops = filter_c2cdiops(cache->cache_filter);
+
+ if ( S_ISREG(mode) && fiops && fiops->permission ) {
+ EXIT;
+ return fiops->permission(inode, mask);
+ }
+ if ( S_ISDIR(mode) && diops && diops->permission ) {
+ EXIT;
+ return diops->permission(inode, mask);
+ }
+ }
+
+ /* The cache filesystem doesn't have its own permission function,
+ * but we don't want to duplicate the VFS code here. In order
+ * to avoid looping from permission calling this function again,
+ * we temporarily override the permission operation while we call
+ * the VFS permission function.
+ */
+ inode->i_op->permission = NULL;
+ rc = permission(inode, mask);
+ inode->i_op->permission = &presto_permission;
+
+ EXIT;
+ return rc;
+}
+
+
+static int presto_dir_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct dentry *de = file->f_dentry;
+ struct file_operations *fops;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ int minor;
+ int error;
+
+ ENTRY;
+
+ error = presto_prep(file->f_dentry, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ make_bad_inode(inode);
+ return error;
+ }
+ minor = presto_c2m(cache);
+
+ CDEBUG(D_CACHE, "minor %d, DATA_OK: %d, ino: %ld\n",
+ minor, presto_chk(de, PRESTO_DATA), inode->i_ino);
+
+ if ( ISLENTO(minor) )
+ goto cache;
+
+ if ( !presto_chk(de, PRESTO_DATA) ) {
+ CDEBUG(D_CACHE, "doing lento_opendir\n");
+ rc = presto_opendir_upcall(minor, file->f_dentry, fset->fset_mtpt, SYNCHRONOUS);
+ }
+
+ if ( rc ) {
+ printk("presto_dir_open: DATA_OK: %d, ino: %ld, error %d\n",
+ presto_chk(de, PRESTO_DATA), inode->i_ino, rc);
+ return rc ;
+ }
+
+ cache:
+ fops = filter_c2cdfops(cache->cache_filter);
+ if ( fops->open ) {
+ rc = fops->open(inode, file);
+ }
+ presto_set(de, PRESTO_DATA | PRESTO_ATTR);
+ CDEBUG(D_CACHE, "returns %d, data %d, attr %d\n", rc,
+ presto_chk(de, PRESTO_DATA), presto_chk(de, PRESTO_ATTR));
+ return 0;
+}
+
+struct file_operations presto_dir_fops = {
+ open: presto_dir_open
+};
+
+struct inode_operations presto_dir_iops = {
+ create: presto_create,
+ lookup: presto_lookup,
+ link: presto_link,
+ unlink: presto_unlink,
+ symlink: presto_symlink,
+ mkdir: presto_mkdir,
+ rmdir: presto_rmdir,
+ mknod: presto_mknod,
+ rename: presto_rename,
+ permission: presto_permission,
+ setattr: presto_setattr,
+#ifdef CONFIG_FS_EXT_ATTR
+ set_ext_attr: presto_set_ext_attr,
+#endif
+
+};
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)