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

Next file: linux/fs/coda/inode.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.108/linux/fs/coda/dir.c linux/fs/coda/dir.c
@@ -27,6 +27,7 @@
 
 /* dir inode-ops */
 static int coda_create(struct inode *dir, struct dentry *new, int mode);
+static int coda_mknod(struct inode *dir, struct dentry *new, int mode, int rdev);
 static int coda_lookup(struct inode *dir, struct dentry *target);
 static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, 
 		     struct dentry *entry);
@@ -42,20 +43,24 @@
 static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
 
 /* dentry ops */
-int coda_dentry_revalidate(struct dentry *de);
-
+static int coda_dentry_revalidate(struct dentry *de);
+static void coda_dentry_delete(struct dentry *);
 /* support routines */
 static int coda_venus_readdir(struct file *filp, void *dirent, 
 			      filldir_t filldir);
 int coda_fsync(struct file *, struct dentry *dentry);
 static int coda_refresh_inode(struct dentry *dentry);
 
+int coda_crossvol_rename = 0;
+int coda_hasmknod = 0;
+
+
 struct dentry_operations coda_dentry_operations =
 {
 	coda_dentry_revalidate, /* revalidate */
 	NULL, /* hash */
-	NULL,
-	NULL,
+	NULL, /* compare */
+	coda_dentry_delete /* delete */
 };
 
 struct inode_operations coda_dir_inode_operations =
@@ -68,7 +73,7 @@
 	coda_symlink,	        /* symlink */
 	coda_mkdir,	        /* mkdir */
 	coda_rmdir,   	        /* rmdir */
-	NULL,		        /* mknod */
+	coda_mknod,	        /* mknod */
 	coda_rename,	        /* rename */
 	NULL,	                /* readlink */
 	NULL,	                /* follow_link */
@@ -100,7 +105,7 @@
 
 
 /* inode operations for directories */
-/* access routines: lookup, readlink, permission */
+/* acces routines: lookup, readlink, permission */
 static int coda_lookup(struct inode *dir, struct dentry *entry)
 {
         struct coda_inode_info *dircnp;
@@ -118,7 +123,7 @@
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("coda_lookup: inode is NULL or not a directory\n");
-		return -ENOENT;
+		return -ENOTDIR;
 	}
 
 	dircnp = ITOC(dir);
@@ -154,7 +159,7 @@
 		}
 	    	error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
 		if (error)
-			return -error;
+			return error;
 	} else if (error != -ENOENT) {
 	        CDEBUG(D_INODE, "error for %s(%*s)%d\n",
 		       coda_f2s(&dircnp->c_fid), length, name, error);
@@ -215,7 +220,7 @@
 
 
 
-/* creation routines: create, mkdir, link, symlink */
+/* creation routines: create, mknod, mkdir, link, symlink */
 
 static int coda_create(struct inode *dir, struct dentry *de, int mode)
 {
@@ -227,6 +232,7 @@
 	struct ViceFid newfid;
 	struct coda_vattr attrs;
 
+	ENTRY;
 	coda_vfs_stat.create++;
 
 	CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode);
@@ -249,7 +255,7 @@
         }
 
 	error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 
-				0, mode, &newfid, &attrs);
+				0, mode, 0, &newfid, &attrs);
 
         if ( error ) {
 		CDEBUG(D_INODE, "create: %s, result %d\n",
@@ -271,6 +277,63 @@
         return 0;
 }			     
 
+static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev)
+{
+        int error=0;
+        struct coda_inode_info *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;
+
+	if ( coda_hasmknod == 0 )
+		return -EIO;
+
+	coda_vfs_stat.create++;
+
+	CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n",name, length, mode, rdev);
+
+        if (!dir || !S_ISDIR(dir->i_mode)) {
+                printk("coda_mknod: inode is null or not a directory\n");
+                return -ENOENT;
+        }
+
+	if (coda_isroot(dir) && coda_iscontrol(name, length))
+		return -EPERM;
+
+	dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        if ( length > CFS_MAXNAMLEN ) {
+		printk("name too long: mknod, %s(%s)\n", 
+		       coda_f2s(&dircnp->c_fid), name);
+		return -ENAMETOOLONG;
+        }
+
+	error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 
+				0, mode, rdev, &newfid, &attrs);
+
+        if ( error ) {
+		CDEBUG(D_INODE, "mknod: %s, result %d\n",
+		       coda_f2s(&newfid), error); 
+		d_drop(de);
+		return error;
+	}
+
+	error = coda_cnode_make(&result, &newfid, dir->i_sb);
+	if ( error ) {
+		d_drop(de);
+		result = NULL;
+		return error;
+	}
+
+	/* invalidate the directory cnode's attributes */
+	dircnp->c_flags |= C_VATTR;
+	d_instantiate(de, result);
+        return 0;
+}			     
+
 static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
 {
         struct coda_inode_info *dircnp;
@@ -281,7 +344,7 @@
 	int error;
 	struct ViceFid newfid;
 
-
+	ENTRY;
 	coda_vfs_stat.mkdir++;
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -345,7 +408,6 @@
 		return -EPERM;
 
         dir_cnp = ITOC(dir_inode);
-        CHECK_CNODE(dir_cnp);
         cnp = ITOC(inode);
         CHECK_CNODE(cnp);
 
@@ -362,8 +424,9 @@
 
 	if (  ! error ) { 
 		dir_cnp->c_flags |= C_VATTR;
-		inode->i_nlink++;
+		++inode->i_count;
 		d_instantiate(de, inode);
+		inode->i_nlink++;
 	} else {
 		d_drop(de);
 	}
@@ -417,7 +480,6 @@
 }
 
 /* destruction routines: unlink, rmdir */
-
 int coda_unlink(struct inode *dir, struct dentry *de)
 {
         struct coda_inode_info *dircnp;
@@ -445,8 +507,8 @@
 
         /* cache management */
 	dircnp->c_flags |= C_VATTR;
-	
 	de->d_inode->i_nlink--;
+
 	d_delete(de);
 
         return 0;
@@ -459,6 +521,7 @@
 	int len = de->d_name.len;
         int error, rehash = 0;
 
+	ENTRY;
 	coda_vfs_stat.rmdir++;
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -516,54 +579,86 @@
 	struct inode *new_inode = new_dentry->d_inode;
         struct coda_inode_info *new_cnp, *old_cnp;
         int error, rehash = 0, update = 1;
-ENTRY;
+
+	ENTRY;
 	coda_vfs_stat.rename++;
 
+        if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
+                return -ENAMETOOLONG;
+        }
+
         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));
+        CDEBUG(D_INODE, "old: %s, (%d length, %d strlen), new: %s"
+	       "(%d length, %d strlen).old:d_count: %d, new:d_count: %d\n", 
+	       old_name, old_length, strlen(old_name), new_name, new_length, 
+	       strlen(new_name),old_dentry->d_count, new_dentry->d_count);
 
-        if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
-                return -ENAMETOOLONG;
+	if (new_inode == old_inode)
+		return 0;
+
+	if (new_dir == old_dir)
+		goto do_rename;
+	/* make sure target is not in use */
+	if (new_inode && S_ISDIR(new_inode->i_mode)) { 
+		/*
+                 * Prune any children before testing for busy.
+                 */
+                if (new_dentry->d_count > 1)
+                        shrink_dcache_parent(new_dentry);
+
+                if (new_dentry->d_count > 1)
+                        return -EBUSY;
         }
 
-        /* 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 ( coda_crossvol_rename == 0 && 
+	     old_cnp->c_fid.Volume != new_cnp->c_fid.Volume )
+		return -EXDEV;
+
+	/* if the volumeid are the same we can reuse the inode,
+	   otherwise we need a new inode, since the new file 
+	   will have a new inode number. */
+
+	/* if moving a directory, clean the dcache */
+	if (S_ISDIR(old_inode->i_mode) && old_dentry->d_count > 1) 
+		shrink_dcache_parent(old_dentry);
+
+#if 0
+	if (old_dentry->d_count > 1) {
+		return -EBUSY;
+	}
+#endif
+
+	if (new_dentry->d_count > 1) {
+		return -EBUSY;
+	}
+	d_drop(old_dentry);
+	update = 0;
+
+ do_rename:
 	if (!list_empty(&new_dentry->d_hash)) {
-	        d_drop(new_dentry);
-		rehash = 1;
+		d_drop(new_dentry);
+		rehash = update;
 	}
+	if ( new_inode ) 
+		d_delete(new_dentry);
 
         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 (rehash) {
+		d_add(new_dentry, NULL);
+	}
 	if (update)
 	        d_move(old_dentry, new_dentry);
-         
+	
         CDEBUG(D_INODE, "result %d\n", error); 
 
 	EXIT;
@@ -594,7 +689,7 @@
         
         if ( !cnp->c_ovp ) {
                 CDEBUG(D_FILE, "open inode pointer = NULL.\n");
-                return -ENODEV;
+                return -EIO;
         }
 
 	coda_prepare_openfile(inode, file, cnp->c_ovp, &open_file,
@@ -620,17 +715,16 @@
         struct coda_inode_info *cnp;
         int error = 0;
         struct inode *cont_inode = NULL;
-        unsigned short flags = f->f_flags;
+        unsigned short flags = f->f_flags & (~O_EXCL);
 	unsigned short coda_flags = coda_flags_to_cflags(flags);
 
         ENTRY;
 	coda_vfs_stat.open++;
         
-        CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n", 
-	       f->f_dentry->d_inode->i_ino, flags);
+        CDEBUG(D_SPECIAL, "OPEN inode number: %ld, count %d, flags %o.\n", 
+	       f->f_dentry->d_inode->i_ino, f->f_dentry->d_count, flags);
 
         cnp = ITOC(i);
-        CHECK_CNODE(cnp);
 
 	error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev); 
 	if (error) {
@@ -641,26 +735,22 @@
 
         /* coda_upcall returns ino number of cached object, get inode */
         CDEBUG(D_FILE, "cache file dev %d, ino %ld\n", dev, ino);
+	error = coda_inode_grab(dev, ino, &cont_inode);
+	
+	if ( error || !cont_inode ){
+		printk("coda_open: coda_inode_grab error %d.", error);
+		if (cont_inode) 
+			iput(cont_inode);
+		return error;
+	}
 
-        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; 
-        } 
+        if (  cnp->c_ovp ) {
+		iput(cnp->c_ovp);
+		cnp->c_ovp = NULL;
+	}
+	cnp->c_ovp = 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", 
@@ -674,7 +764,7 @@
 {
         struct coda_inode_info *cnp;
         int error;
-        unsigned short flags = f->f_flags;
+        unsigned short flags = (f->f_flags) & (~O_EXCL);
 	unsigned short cflags = coda_flags_to_cflags(flags);
 
         ENTRY;
@@ -805,7 +895,8 @@
         return error;
 }
 
-int coda_dentry_revalidate(struct dentry *de)
+/* called when a cache lookup succeeds */
+static int coda_dentry_revalidate(struct dentry *de)
 {
 	int valid = 1;
 	struct inode *inode = de->d_inode;
@@ -816,12 +907,32 @@
 		if (is_bad_inode(inode))
 			return 0;
 		cii = ITOC(de->d_inode);
-		if (cii->c_flags & (C_PURGE | C_VATTR)) 
+		if (cii->c_flags & C_PURGE) 
 			valid = 0;
 	}
 	return valid ||  coda_isroot(de->d_inode);
 }
 
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static void coda_dentry_delete(struct dentry * dentry)
+{
+	int flags;
+
+	if (!dentry->d_inode) 
+		return ;
+
+	flags =  (ITOC(dentry->d_inode)->c_flags) & C_PURGE;
+	if (is_bad_inode(dentry->d_inode) || flags) {
+		CDEBUG(D_DOWNCALL, "bad inode, unhashing %s/%s, %ld\n", 
+		       dentry->d_parent->d_name.name, dentry->d_name.name,
+		       dentry->d_inode->i_ino);
+		d_drop(dentry);
+	}
+}
+
 
 static int coda_refresh_inode(struct dentry *dentry)
 {
@@ -831,15 +942,16 @@
 	ino_t old_ino;
 	struct inode *inode = dentry->d_inode;
 	struct coda_inode_info *cii = ITOC(inode);
-	
+
 	ENTRY;
+	
 	error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
 	if ( error ) { 
 		make_bad_inode(inode);
 		return -EIO;
 	}
 
-	/* this baby may be lost if:
+	/* this inode may be lost if:
             - it's type changed
             - it's ino changed 
 	*/
@@ -854,7 +966,7 @@
 		return -EIO;
 	}
 	
-	cii->c_flags &= ~(C_VATTR | C_PURGE);
+	cii->c_flags &= ~C_VATTR;
 	return 0;
 }
 
@@ -876,7 +988,7 @@
 	       dentry->d_name.len, dentry->d_name.name,
 	       dentry->d_parent->d_name.len, dentry->d_parent->d_name.name);
 
-	if ( cii->c_flags & (C_VATTR | C_PURGE )) {
+	if (cii->c_flags & (C_VATTR | C_PURGE)) {
 		error = coda_refresh_inode(dentry);
 	}
 

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