patch-2.4.6 linux/fs/nfsd/nfsfh.c

Next file: linux/fs/nfsd/nfsproc.c
Previous file: linux/fs/nfsd/nfs3xdr.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.5/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
@@ -134,7 +134,11 @@
 	struct inode *inode;
 	struct list_head *lp;
 	struct dentry *result;
+	if (ino == 0)
+		return ERR_PTR(-ESTALE);
 	inode = iget(sb, ino);
+	if (inode == NULL)
+		return ERR_PTR(-ENOMEM);
 	if (is_bad_inode(inode)
 	    || (generation && inode->i_generation != generation)
 		) {
@@ -169,10 +173,34 @@
 		return ERR_PTR(-ENOMEM);
 	}
 	result->d_flags |= DCACHE_NFSD_DISCONNECTED;
-	d_rehash(result); /* so a dput won't loose it */
 	return result;
 }
 
+static struct dentry *nfsd_get_dentry(struct super_block *sb, __u32 *fh,
+					     int len, int fhtype, int parent)
+{
+	if (sb->s_op->fh_to_dentry)
+		return sb->s_op->fh_to_dentry(sb, fh, len, fhtype, parent);
+	switch (fhtype) {
+	case 1:
+		if (len < 2)
+			break;
+		if (parent)
+			break;
+		return nfsd_iget(sb, fh[0], fh[1]);
+
+	case 2:
+		if (len < 3)
+			break;
+		if (parent)
+			return nfsd_iget(sb,fh[2],0);
+		return nfsd_iget(sb,fh[0],fh[1]);
+	default: break;
+	}
+	return ERR_PTR(-EINVAL);
+}
+
+
 /* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent"
  * as a parent and "name" as a name
  * It should possibly go in dcache.c
@@ -196,7 +224,7 @@
 	 * make it an IS_ROOT instead
 	 */
 	spin_lock(&dcache_lock);
-	list_del(&tdentry->d_child);
+	list_del_init(&tdentry->d_child);
 	tdentry->d_parent = tdentry;
 	spin_unlock(&dcache_lock);
 	d_rehash(target);
@@ -270,10 +298,10 @@
 		}
 		spin_unlock(&dcache_lock);
 		if (pdentry == NULL) {
-			pdentry = d_alloc_root(igrab(tdentry->d_inode));
+			pdentry = d_alloc_root(tdentry->d_inode);
 			if (pdentry) {
+				igrab(tdentry->d_inode);
 				pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
-				d_rehash(pdentry);
 			}
 		}
 		if (pdentry == NULL)
@@ -347,11 +375,10 @@
  * connection if made.
  */
 static struct dentry *
-find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, int needpath)
+find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int needpath)
 {
 	struct dentry *dentry, *result = NULL;
 	struct dentry *tmp;
-	int  found =0;
 	int err = -ESTALE;
 	/* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)
 	 * dcache path ever exists, as otherwise two partial paths might get
@@ -367,7 +394,7 @@
 	 */
  retry:
 	down(&sb->s_nfsd_free_path_sem);
-	result = nfsd_iget(sb, ino, generation);
+	result = nfsd_get_dentry(sb, datap, len, fhtype, 0);
 	if (IS_ERR(result)
 	    || !(result->d_flags & DCACHE_NFSD_DISCONNECTED)
 	    || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) {
@@ -384,16 +411,12 @@
 	/* It's a directory, or we are required to confirm the file's
 	 * location in the tree.
 	 */
-	dprintk("nfs_fh: need to look harder for %d/%ld\n",sb->s_dev,ino);
+	dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,datap[0]);
 
-	found = 0;
 	if (!S_ISDIR(result->d_inode->i_mode)) {
 		nfsdstats.fh_nocache_nondir++;
-		if (dirino == 0)
-			goto err_result; /* don't know how to find parent */
-		else {
 			/* need to iget dirino and make sure this inode is in that directory */
-			dentry = nfsd_iget(sb, dirino, 0);
+			dentry = nfsd_get_dentry(sb, datap, len, fhtype, 1);
 			err = PTR_ERR(dentry);
 			if (IS_ERR(dentry))
 				goto err_result;
@@ -402,8 +425,6 @@
 			    || !S_ISDIR(dentry->d_inode->i_mode)) {
 				goto err_dentry;
 			}
-			if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
-				found = 1;
 			tmp = splice(result, dentry);
 			err = PTR_ERR(tmp);
 			if (IS_ERR(tmp))
@@ -413,15 +434,13 @@
 				d_drop(result);
 				dput(result);
 				result = tmp;
-				/* If !found, then this is really weird, but it shouldn't hurt */
 			}
-		}
 	} else {
 		nfsdstats.fh_nocache_dir++;
 		dentry = dget(result);
 	}
 
-	while(!found) {
+	while(dentry->d_flags & DCACHE_NFSD_DISCONNECTED) {
 		/* LOOP INVARIANT */
 		/* haven't found a place in the tree yet, but we do have a free path
 		 * from dentry down to result, and dentry is a directory.
@@ -440,12 +459,9 @@
 			goto err_dentry;
 		}
 
-		if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
-			found = 1;
-
 		tmp = splice(dentry, pdentry);
 		if (tmp != dentry) {
-			/* Something wrong.  We need to drop thw whole dentry->result path
+			/* Something wrong.  We need to drop the whole dentry->result path
 			 * whatever it was
 			 */
 			struct dentry *d;
@@ -580,31 +596,23 @@
 			case 0:
 				dentry = dget(exp->ex_dentry);
 				break;
-			case 1:
-				if ((data_left-=2)<0) goto out;
-				dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
-							datap[0], datap[1],
-							0,
-							!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
-				break;
-			case 2:
-				if ((data_left-=3)<0) goto out;
+			default:
 				dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
-							datap[0], datap[1],
-							datap[2],
+							datap, data_left, fh->fh_fileid_type,
 							!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
-				break;
-			default: goto out;
 			}
 		} else {
-
+			__u32 tfh[3];
+			tfh[0] = fh->ofh_ino;
+			tfh[1] = fh->ofh_generation;
+			tfh[2] = fh->ofh_dirino;
 			dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
-						fh->ofh_ino, fh->ofh_generation,
-						fh->ofh_dirino,
+						tfh, 3, fh->ofh_dirino?2:1,
 						!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
 		}
 		if (IS_ERR(dentry)) {
-			error = nfserrno(PTR_ERR(dentry));
+			if (PTR_ERR(dentry) != EINVAL)
+				error = nfserrno(PTR_ERR(dentry));
 			goto out;
 		}
 #ifdef NFSD_PARANOIA
@@ -709,9 +717,20 @@
 		      __u32 **datapp, int maxsize)
 {
 	__u32 *datap= *datapp;
+	struct super_block *sb = dentry->d_inode->i_sb;
+	
 	if (dentry == exp->ex_dentry)
 		return 0;
-	/* if super_operations provides dentry_to_fh lookup, should use that */
+
+	if (sb->s_op->dentry_to_fh) {
+		int need_parent = !S_ISDIR(dentry->d_inode->i_mode) &&
+			!(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
+		
+		int type = sb->s_op->dentry_to_fh(dentry, datap, &maxsize, need_parent);
+		datap += maxsize;
+		*datapp = datap;
+		return type;
+	}
 	
 	*datap++ = ino_t_to_u32(dentry->d_inode->i_ino);
 	*datap++ = dentry->d_inode->i_generation;
@@ -724,9 +743,31 @@
 	return 2;
 }
 
+/*
+ * for composing old style file handles
+ */
+inline void _fh_update_old(struct dentry *dentry, struct svc_export *exp,
+			   struct knfsd_fh *fh)
+{
+	fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino);
+	fh->ofh_generation = dentry->d_inode->i_generation;
+	if (S_ISDIR(dentry->d_inode->i_mode) ||
+	    (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
+		fh->ofh_dirino = 0;
+}
+
 int
-fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
+fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, struct svc_fh *ref_fh)
 {
+	/* ref_fh is a reference file handle.
+	 * if it is non-null, then we should compose a filehandle which is
+	 * of the same version, where possible.
+	 * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
+	 * Then create a 32byte filehandle using nfs_fhbase_old
+	 * But only do this if dentry_to_fh is not available
+	 *
+	 */
+	
 	struct inode * inode = dentry->d_inode;
 	struct dentry *parent = dentry->d_parent;
 	__u32 *datap;
@@ -747,20 +788,32 @@
 	fhp->fh_dentry = dentry; /* our internal copy */
 	fhp->fh_export = exp;
 
-	fhp->fh_handle.fh_version = 1;
-	fhp->fh_handle.fh_auth_type = 0;
-	fhp->fh_handle.fh_fsid_type = 0;
-	datap = fhp->fh_handle.fh_auth+0;
-	/* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
-	*datap++ = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
-	*datap++ = ino_t_to_u32(exp->ex_ino);
-
-	if (inode)
-		fhp->fh_handle.fh_fileid_type =
-			_fh_update(dentry, exp, &datap, fhp->fh_maxsize-3);
-
-	fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
-
+	if (ref_fh &&
+	    ref_fh->fh_handle.fh_version == 0xca &&
+	    parent->d_inode->i_sb->s_op->dentry_to_fh == NULL) {
+		/* old style filehandle please */
+		memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
+		fhp->fh_handle.fh_size = NFS_FHSIZE;
+		fhp->fh_handle.ofh_dcookie = 0xfeebbaca;
+		fhp->fh_handle.ofh_dev =  htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
+		fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
+		fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_ino);
+		fhp->fh_handle.ofh_dirino = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
+		if (inode)
+			_fh_update_old(dentry, exp, &fhp->fh_handle);
+	} else {
+		fhp->fh_handle.fh_version = 1;
+		fhp->fh_handle.fh_auth_type = 0;
+		fhp->fh_handle.fh_fsid_type = 0;
+		datap = fhp->fh_handle.fh_auth+0;
+		/* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
+		*datap++ = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
+		*datap++ = ino_t_to_u32(exp->ex_ino);
+		if (inode)
+			fhp->fh_handle.fh_fileid_type =
+				_fh_update(dentry, exp, &datap, fhp->fh_maxsize-3);
+		fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
+	}
 
 	nfsd_nr_verified++;
 	if (fhp->fh_handle.fh_fileid_type == 255)
@@ -786,11 +839,15 @@
 		goto out_negative;
 	if (fhp->fh_handle.fh_fileid_type != 0)
 		goto out_uptodate;
-	datap = fhp->fh_handle.fh_auth+
-		          fhp->fh_handle.fh_size/4 -1;
-	fhp->fh_handle.fh_fileid_type =
-		_fh_update(dentry, fhp->fh_export, &datap, fhp->fh_maxsize-fhp->fh_handle.fh_size);
-	fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
+	if (fhp->fh_handle.fh_version != 1) {
+		_fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
+	} else {
+		datap = fhp->fh_handle.fh_auth+
+			fhp->fh_handle.fh_size/4 -1;
+		fhp->fh_handle.fh_fileid_type =
+			_fh_update(dentry, fhp->fh_export, &datap, fhp->fh_maxsize-fhp->fh_handle.fh_size);
+		fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
+	}
 out:
 	return 0;
 

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