patch-2.1.70 linux/fs/coda/dir.c

Next file: linux/fs/coda/file.c
Previous file: linux/fs/coda/coda_linux.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.69/linux/fs/coda/dir.c linux/fs/coda/dir.c
@@ -0,0 +1,769 @@
+/*
+ * Direcotry operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cnode.h>
+#include <linux/coda_namecache.h>
+
+/* dir inode-ops */
+static int coda_create(struct inode *dir, struct dentry *new, int mode);
+static int coda_lookup(struct inode *dir, struct dentry *target);
+static int coda_link(struct inode *old_inode, struct inode *dir_inode, 
+		     struct dentry *entry);
+static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
+static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
+			const char *symname);
+static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, int mode);
+static int coda_rmdir(struct inode *dir_inode, struct dentry *entry);
+static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, 
+                       struct inode *new_inode, struct dentry *new_dentry);
+
+/* dir file-ops */
+static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
+
+/* support routines */
+static int coda_venus_readdir(struct file *filp, void *dirent, 
+			      filldir_t filldir);
+
+
+
+struct inode_operations coda_dir_inode_operations =
+{
+	&coda_dir_operations,
+	coda_create,	        /* create */
+	coda_lookup,	        /* lookup */
+	coda_link,	        /* link */
+	coda_unlink,            /* unlink */
+	coda_symlink,	        /* symlink */
+	coda_mkdir,	        /* mkdir */
+	coda_rmdir,   	        /* rmdir */
+	NULL,		        /* mknod */
+	coda_rename,	        /* rename */
+	NULL,	                /* readlink */
+	NULL,	                /* follow_link */
+	NULL,	                /* readpage */
+	NULL,		        /* writepage */
+	NULL,		        /* bmap */
+	NULL,	                /* truncate */
+	coda_permission,        /* permission */
+	NULL,                   /* smap */
+	NULL,                   /* update page */
+        NULL                    /* revalidate */
+};
+
+struct file_operations coda_dir_operations = {
+        NULL,                   /* lseek */
+        NULL,                   /* read -- bad  */
+        NULL,                   /* write */
+        coda_readdir,           /* readdir */
+        NULL,                   /* select */
+        NULL,                   /* ioctl */
+        NULL,                   /* mmap */
+        coda_open,              /* open */
+        coda_release,           /* release */
+        NULL,                   /* fsync */
+};
+
+
+/* inode operations for directories */
+/* acces routines: lookup, readlink, permission */
+static int coda_lookup(struct inode *dir, struct dentry *entry)
+{
+        struct cnode *dircnp, *savedcnp;
+	struct inode *res_inode = NULL;
+	struct ViceFid resfid;
+	int type;
+	int error = 0;
+	const char *name = entry->d_name.name;
+	size_t length = entry->d_name.len;
+	char str[50];
+	
+        ENTRY;
+        CDEBUG(D_INODE, "name %s, len %d in ino %ld\n", 
+	       name, length, dir->i_ino);
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("coda_lookup: inode is NULL or not a directory\n");
+		return -ENOENT;
+	}
+
+	dircnp = ITOC(dir);
+	CHECK_CNODE(dircnp);
+
+	if ( length > CFS_MAXNAMLEN ) {
+	        printk("name too long: lookup, %s (%s)\n", 
+		       coda_f2s(&dircnp->c_fid, str), name);
+		return -ENAMETOOLONG;
+	}
+	
+	CDEBUG(D_INODE, "lookup: %s in %s\n", name, 
+	       coda_f2s(&dircnp->c_fid, str));
+
+        /* control object, create inode on the fly */
+        if ( coda_isroot(dir) && (CFS_CONTROLLEN == length) && 
+	     (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0) ) {
+	        error = coda_cnode_makectl(&res_inode, dir->i_sb);
+		CDEBUG(D_SPECIAL, 
+		       "Lookup on CTL object; iput of ino %ld, count %d\n", 
+		       dir->i_ino, dir->i_count);
+                goto exit;
+        }
+
+        /* do we have it already in name cache */
+	if ( (savedcnp = cfsnc_lookup(dircnp, name, length)) != NULL ) {
+		CHECK_CNODE(savedcnp);
+		res_inode = CTOI(savedcnp);
+		iget(res_inode->i_sb, res_inode->i_ino);
+		CDEBUG(D_INODE, "cache hit for ino: %ld, count: %d!\n",
+		       res_inode->i_ino, res_inode->i_count);
+		goto exit;
+	}
+	CDEBUG(D_INODE, "name not found in cache!\n");
+
+        /* name not cached */
+        error = venus_lookup(dir->i_sb, &(dircnp->c_fid), 
+				(const char *)name, length, &type, &resfid);
+
+	res_inode = NULL;
+	if (!error) {
+	    	error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
+		if (error)
+			return -EACCES;
+		/* put the thing in the name cache */
+		savedcnp = ITOC(res_inode);
+		CHECK_CNODE(savedcnp);
+		CDEBUG(D_INODE, "ABOUT to enter into cache.\n");
+		cfsnc_enter(dircnp, name, length, savedcnp);
+		CDEBUG(D_INODE, "entered in cache\n");
+	} else if (error != -ENOENT) {
+	        CDEBUG(D_INODE, "error for %s(%s)%d\n",
+		       coda_f2s(&dircnp->c_fid, str), name, error);
+		return error;
+	}
+	CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d\n",
+	       name, coda_f2s(&resfid, str), type, error);
+
+	/* at last we have our inode number from Venus, 
+	   now allocate storage for
+	   the cnode and do iget, and fill in the attributes */
+
+
+exit:
+	entry->d_time = 0;
+	entry->d_op = NULL;
+	d_add(entry, res_inode);
+        EXIT;
+        return 0;
+}
+
+
+
+int coda_permission(struct inode *inode, int mask)
+{
+        struct cnode *cp;
+        int error;
+        int mode = inode->i_mode;
+	char str[50];
+ 
+        ENTRY;
+
+        if ( mask == 0 ) {
+                EXIT;
+                return 0;
+        }
+
+        /* we should be able to trust what is in the mode
+           although Venus should be told to return the 
+           correct modes to the kernel */
+        if ( coda_access_cache == 1 ) { 
+            if (current->fsuid == inode->i_uid)
+                mode >>= 6;
+        	else if (in_group_p(inode->i_gid))
+                mode >>= 3;
+        	if (((mode & mask & 0007) == mask) )
+                return 0;
+	}
+
+        cp = ITOC(inode);
+        CHECK_CNODE(cp);
+
+        CDEBUG(D_INODE, "mask is %o\n", mask);
+        error = venus_access(inode->i_sb, &(cp->c_fid), mask);
+    
+        CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n", 
+	       coda_f2s(&(cp->c_fid), str), inode->i_ino, mask, error);
+
+        return error; 
+}
+
+
+
+/* creation routines: create, mkdir, link, symlink */
+
+static int coda_create(struct inode *dir, struct dentry *de, int mode)
+{
+        int error=0;
+        struct cnode *dircnp;
+	const char *name=de->d_name.name;
+	int length=de->d_name.len;
+	struct inode *result = NULL;
+	struct ViceFid newfid;
+	struct coda_vattr attrs;
+
+	CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode);
+
+        if (!dir || !S_ISDIR(dir->i_mode)) {
+                printk("coda_create: inode is null or not a directory\n");
+                return -ENOENT;
+        }
+	dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        if ( length > CFS_MAXNAMLEN ) {
+		char str[50];
+		printk("name too long: create, %s(%s)\n", 
+		       coda_f2s(&dircnp->c_fid, str), name);
+		return -ENAMETOOLONG;
+        }
+
+	error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 
+				0, mode, &newfid, &attrs);
+
+        if ( error ) {
+		char str[50];
+		CDEBUG(D_INODE, "create: %s, result %d\n",
+		       coda_f2s(&newfid, str), error); 
+		return error;
+	}
+
+	error = coda_cnode_make(&result, &newfid, dir->i_sb);
+	if ( error ) {
+		result = NULL;
+		return error;
+	}
+
+	/* invalidate the directory cnode's attributes */
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+
+	d_instantiate(de, result);
+        return 0;
+}			     
+
+static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
+{
+        struct cnode *dircnp;
+	struct inode *inode;
+	struct coda_vattr attr;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+	int error;
+	struct ViceFid newfid;
+	char fidstr[50];
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("coda_mkdir: inode is NULL or not a directory\n");
+		return -ENOENT;
+	}
+        dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        if ( len > CFS_MAXNAMLEN )
+                return -ENAMETOOLONG;
+
+	CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", 
+	       name, len, coda_f2s(&(dircnp->c_fid), fidstr), mode);
+
+	attr.va_mode = mode;
+	error = venus_mkdir(dir->i_sb, &(dircnp->c_fid), 
+			       name, len, &newfid, &attr);
+        
+        if ( error ) {
+	        CDEBUG(D_INODE, "mkdir error: %s result %d\n", 
+		       coda_f2s(&newfid, fidstr), error); 
+                return error;
+        }
+         
+	CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n", 
+	       coda_f2s(&newfid, fidstr)); 
+
+	error = coda_cnode_make(&inode, &newfid, dir->i_sb);
+	if ( error ) 
+	       return error;
+	
+	/* invalidate the directory cnode's attributes */
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+
+	dir->i_nlink++;
+	d_instantiate(de, inode);
+        return 0;
+}
+
+static int coda_link(struct inode *inode, struct inode *dir_inode, 
+	  struct dentry *de)
+{
+        const char * name = de->d_name.name;
+	int len = de->d_name.len;
+        struct cnode *dir_cnp, *cnp;
+	char str[50];
+	int error;
+
+        ENTRY;
+
+        dir_cnp = ITOC(dir_inode);
+        CHECK_CNODE(dir_cnp);
+        cnp = ITOC(inode);
+        CHECK_CNODE(cnp);
+
+	CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid), str));
+	CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid), str));
+
+        if ( len > CFS_MAXNAMLEN ) {
+                printk("coda_link: name too long. \n");
+                return -ENAMETOOLONG;
+        }
+
+        /* Check for link to/from control object. */
+
+        error = venus_link(dir_inode->i_sb,&(cnp->c_fid), &(dir_cnp->c_fid), 
+			   (const char *)name, len);
+
+	if (  ! error ) { 
+	      dir_cnp->c_flags &= ~C_VATTR;
+	      cfsnc_zapfid(&(dir_cnp->c_fid));
+	      cfsnc_zapfid(&(cnp->c_fid));
+
+	      inode->i_nlink++;
+	      d_instantiate(de, inode);
+	}
+        CDEBUG(D_INODE, "link result %d\n",error);
+	EXIT;
+        return(error);
+}
+
+
+static int 
+coda_symlink(struct inode *dir_inode, struct dentry *de,
+             const char *symname)
+{
+        const char *name = de->d_name.name;
+	int len = de->d_name.len;
+        struct cnode *dir_cnp = ITOC(dir_inode);
+	int symlen;
+        int error=0;
+        
+        ENTRY;
+
+	error = -ENAMETOOLONG;
+	if ( len > CFS_MAXNAMLEN ) { 
+	        return error;
+	}
+
+	symlen = strlen(symname);
+	if ( symlen > CFS_MAXNAMLEN ) { 
+	        return error;
+	}
+        CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen);
+
+	error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len, 
+			      symname, symlen);
+
+	if ( !error ) {
+	  d_drop(de);
+	}
+
+        CDEBUG(D_INODE, "in symlink result %d\n",error);
+        EXIT;
+        return error;
+}
+
+/* destruction routines: unlink, rmdir */
+
+int coda_unlink(struct inode *dir, struct dentry *de)
+{
+        struct cnode *dircnp;
+        int error;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+	char fidstr[50];
+
+	ENTRY;
+
+        dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        CDEBUG(D_INODE, " %s in %s, ino %ld\n", name , 
+	       coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino);
+
+        /* this file should no longer be in the namecache! */
+        cfsnc_zapfile(dircnp, (const char *)name, len);
+
+        error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len);
+
+        if ( error ) {
+                CDEBUG(D_INODE, "upc returned error %d\n", error);
+                return error;
+        }
+
+        /* cache management */
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+	
+	de->d_inode->i_nlink--;
+	d_delete(de);
+
+        return 0;
+}
+
+int coda_rmdir(struct inode *dir, struct dentry *de)
+{
+        struct cnode *dircnp;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+        int error;
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk("coda_rmdir: inode is NULL or not a directory\n");
+		return -ENOENT;
+	}
+        dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+	if (len > CFS_MAXNAMLEN)
+		return -ENAMETOOLONG;
+
+        /* this directory name should no longer be in the namecache */
+        cfsnc_zapfile(dircnp, (const char *)name, len);
+
+	error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
+
+        if ( error ) {
+                CDEBUG(D_INODE, "upc returned error %d\n", error);
+                return error;
+        }
+
+	dircnp->c_flags &= ~C_VATTR;
+	cfsnc_zapfid(&(dircnp->c_fid));
+
+	dir->i_nlink--;
+	d_delete(de);
+
+        return 0;
+}
+
+/* rename */
+static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, 
+		       struct inode *new_dir, struct dentry *new_dentry)
+{
+        const char *old_name = old_dentry->d_name.name;
+        const char *new_name = new_dentry->d_name.name;
+	int old_length = old_dentry->d_name.len;
+	int new_length = new_dentry->d_name.len;
+	struct inode *old_inode = old_dentry->d_inode;
+	struct inode *new_inode = new_dentry->d_inode;
+        struct cnode *new_cnp, *old_cnp;
+        int error, rehash = 0, update = 1;
+ENTRY;
+        old_cnp = ITOC(old_dir);
+        CHECK_CNODE(old_cnp);
+        new_cnp = ITOC(new_dir);
+        CHECK_CNODE(new_cnp);
+
+        CDEBUG(D_INODE, "old: %s, (%d length, %d strlen), new: %s (%d length, %d strlen).\n", old_name, old_length, strlen(old_name), new_name, new_length, strlen(new_name));
+
+        if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
+                return -ENAMETOOLONG;
+        }
+
+        /* the old file should go from the namecache */
+        cfsnc_zapfile(old_cnp, (const char *)old_name, old_length);
+        cfsnc_zapfile(new_cnp, (const char *)new_name, new_length);
+
+        /* cross directory moves */
+	if (new_dir != old_dir  &&
+	    S_ISDIR(old_inode->i_mode) && 
+	    old_dentry->d_count > 1)
+	        shrink_dcache_parent(old_dentry);
+		
+	/* We must prevent any new references to the
+	 * target while the rename is in progress, so
+	 * we unhash the dentry.  */
+	if (!list_empty(&new_dentry->d_hash)) {
+	        d_drop(new_dentry);
+		rehash = 1;
+	}
+
+        error = venus_rename(old_dir->i_sb, &(old_cnp->c_fid), 
+			     &(new_cnp->c_fid), old_length, new_length, 
+			     (const char *) old_name, (const char *)new_name);
+
+	if (rehash) {
+		d_add(new_dentry, new_inode);
+	}
+
+        if ( error ) {
+                CDEBUG(D_INODE, "returned error %d\n", error);
+                return error;
+        }
+	/* Update the dcache if needed */
+	if (update)
+	        d_move(old_dentry, new_dentry);
+         
+        CDEBUG(D_INODE, "result %d\n", error); 
+
+	EXIT;
+	return 0;
+}
+
+
+
+/* file operations for directories */
+int coda_readdir(struct file *file, void *dirent,  filldir_t filldir)
+{
+        int result = 0;
+        struct cnode *cnp;
+        struct file open_file;
+	struct dentry open_dentry;
+	struct inode *inode=file->f_dentry->d_inode;
+
+        ENTRY;
+
+        if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
+                printk("coda_readdir: inode is NULL or not a directory\n");
+                return -EBADF;
+        }
+
+        cnp = ITOC(inode);
+        CHECK_CNODE(cnp);
+        
+        if ( !cnp->c_ovp ) {
+                CDEBUG(D_FILE, "open inode pointer = NULL.\n");
+                return -ENODEV;
+        }
+
+	coda_prepare_openfile(inode, file, cnp->c_ovp, &open_file,
+			      &open_dentry);
+        if ( S_ISREG(cnp->c_ovp->i_mode) ) {
+                /* Venus: we must read Venus dirents from the file */
+                result = coda_venus_readdir(&open_file, dirent, filldir);
+        } else {
+                /* potemkin case: we are handed a directory inode */
+                result = open_file.f_op->readdir(&open_file, dirent, filldir);
+        }
+	coda_restore_codafile(inode, file, cnp->c_ovp, &open_file);
+	return result;
+        EXIT;
+}
+
+/* ask venus to cache the file and return the inode of the container file,
+   put this inode pointer in the cnode for future read/writes */
+int coda_open(struct inode *i, struct file *f)
+{
+        ino_t ino;
+	dev_t dev;
+        struct cnode *cnp;
+        int error = 0;
+        struct inode *cont_inode = NULL;
+        unsigned short flags = f->f_flags;
+
+        ENTRY;
+        
+        CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n", 
+	       f->f_dentry->d_inode->i_ino, flags);
+
+        if ( flags & O_CREAT ) {
+                flags &= ~O_EXCL; /* taken care of by coda_create ?? */
+        }
+        cnp = ITOC(i);
+        CHECK_CNODE(cnp);
+
+	error = venus_open(i->i_sb, &(cnp->c_fid), flags, &ino, &dev); 
+	if (error) {
+	        CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n",
+		       dev, ino, error);
+		return error;
+	}
+
+        /* coda_upcall returns ino number of cached object, get inode */
+        CDEBUG(D_FILE, "cache file dev %d, ino %ld\n", dev, ino);
+
+        if ( ! cnp->c_ovp ) {
+                error = coda_inode_grab(dev, ino, &cont_inode);
+                
+                if ( error ){
+                        printk("coda_open: coda_inode_grab error %d.", error);
+                        if (cont_inode) iput(cont_inode);
+			return error;
+                }
+                CDEBUG(D_FILE, "GRAB: coda_inode_grab: ino %ld, ops at %x\n", 
+		       cont_inode->i_ino, (int)cont_inode->i_op);
+                cnp->c_ovp = cont_inode; 
+		cnp->c_odentry.d_inode = cont_inode;
+        } 
+        cnp->c_ocount++;
+
+        /* if opened for writing flush cache entry.  */
+        if ( flags & (O_WRONLY | O_RDWR) ) {
+	        cfsnc_zapfid(&(cnp->c_fid));
+	} 
+
+        CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", 
+	       error, i->i_count, i->i_ino);
+        CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %x\n", 
+	       cnp->c_ovp->i_ino, cnp->c_ovp->i_count,
+	       (int)(cnp->c_ovp->i_op));
+        EXIT;
+        return 0;
+}
+
+int coda_release(struct inode *i, struct file *f)
+{
+        struct cnode *cnp;
+        int error;
+        unsigned short flags = f->f_flags;
+
+        ENTRY;
+
+        cnp =ITOC(i);
+        CHECK_CNODE(cnp);
+        CDEBUG(D_FILE,  
+	       "RELEASE coda (ino %ld, ct %d) cache (ino %ld, ct %d)\n",
+               i->i_ino, i->i_count, (cnp->c_ovp ? cnp->c_ovp->i_ino : 0),
+               (cnp->c_ovp ? cnp->c_ovp->i_count : -99));
+
+
+        /* even when c_ocount=0 we cannot put c_ovp to
+         * NULL since the file may be mmapped.
+	 * See code in inode.c (coda_put_inode) for
+	 * further handling of close.
+	 */
+
+        --cnp->c_ocount;
+
+        if (flags & (O_WRONLY | O_RDWR)) {
+                --cnp->c_owrite;
+        }
+
+	error = venus_release(i->i_sb, &(cnp->c_fid), flags);
+
+        CDEBUG(D_FILE, "coda_release: result: %d\n", error);
+        return error;
+}
+
+/* support routines */
+/* 
+ * this structure is manipulated by filldir in vfs layer.
+ * the count holds the remaining amount of space in the getdents buffer,
+ * beyond the current_dir pointer.
+ */
+
+struct getdents_callback {
+	struct linux_dirent * current_dir;
+	struct linux_dirent * previous;
+	int count;
+	int error;
+};
+
+static int coda_venus_readdir(struct file *filp, void *getdent, 
+			      filldir_t filldir)
+{
+        int result = 0,  offset, count, pos, error = 0;
+	int errfill;
+        caddr_t buff = NULL;
+        struct venus_dirent *vdirent;
+        struct getdents_callback *dents_callback;
+        int string_offset;
+        char debug[255];
+
+        ENTRY;        
+
+        /* we also need the ofset of the string in the dirent struct */
+        string_offset = sizeof ( char )* 2  + sizeof(unsigned int) + 
+                        sizeof(unsigned short);
+
+        dents_callback = (struct getdents_callback *) getdent;
+
+        count =  dents_callback->count;
+        CODA_ALLOC(buff, void *, count);
+        if ( ! buff ) { 
+                printk("coda_venus_readdir: out of memory.\n");
+                return -ENOMEM;
+        }
+
+        /* we use this routine to read the file into our buffer */
+        result = read_exec(filp->f_dentry, filp->f_pos, buff, count, 1);
+        if ( result < 0) {
+                printk("coda_venus_readdir: cannot read directory %d.\n",
+		       result);
+                error = result;
+                goto exit;
+        }
+        if ( result == 0) {
+                error = result;
+                goto exit;
+        }
+
+        /* Parse and write into user space. Filldir tells us when done! */
+        offset = filp->f_pos;
+        pos = 0;
+        CDEBUG(D_FILE, "offset %d, count %d.\n", offset, count);
+
+        while ( pos + string_offset < result ) {
+                vdirent = (struct venus_dirent *) (buff + pos);
+
+                /* test if the name is fully in the buffer */
+                if ( pos + string_offset + (int) vdirent->d_namlen >= result ){
+                        break;
+                }
+                
+                /* now we are certain that we can read the entry from buff */
+
+                /* for debugging, get the string out */
+                memcpy(debug, vdirent->d_name, vdirent->d_namlen);
+                *(debug + vdirent->d_namlen) = '\0';
+
+                /* if we don't have a null entry, copy it */
+                if ( vdirent->d_fileno ) {
+                        int namlen  = vdirent->d_namlen;
+                        off_t offs  = filp->f_pos; 
+                        ino_t ino   = vdirent->d_fileno;
+                        char *name  = vdirent->d_name;
+                        /* adjust count */
+                        count = dents_callback->count;
+
+			errfill = filldir(dents_callback,  name, namlen, 
+					  offs, ino); 
+CDEBUG(D_FILE, "ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %s, offset %d, count %d.\n", vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos,  string_offset, debug, (u_int) offs, dents_callback->count);
+
+		      /* errfill means no space for filling in this round */
+                      if ( errfill < 0 ) break;
+                }
+                /* next one */
+                filp->f_pos += (unsigned int) vdirent->d_reclen;
+                pos += (unsigned int) vdirent->d_reclen;
+        } 
+
+exit:
+        CODA_FREE(buff, count);
+        return error;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov