Beispiel #1
0
static int
nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
				          void			  *resp)
{
	struct svc_fh	newfh;
	int		nfserr;

	dprintk("nfsd: SYMLINK  %x/%ld %s -> %s\n",
				SVCFH_DEV(&argp->ffh),
				SVCFH_INO(&argp->ffh),
				argp->fname, argp->tname);

	memset(&newfh, 0, sizeof(newfh));

	/*
	 * Create the link, look up new file and set attrs.
	 */
	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
						 argp->tname, argp->tlen,
						 &newfh);
	if (nfserr)
		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);

	fh_put(&argp->ffh);
	fh_put(&newfh);
	RETURN(nfserr);
}
Beispiel #2
0
int
nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
					struct nfsd3_fhandle_pair *resp)
{
	fh_put(&resp->fh1);
	fh_put(&resp->fh2);
	return 1;
}
Beispiel #3
0
/*
 * Note: we hold the dentry use count while the file is open.
 */
static u32
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
{
	u32		nfserr;
	struct svc_fh	fh;

	/* must initialize before using! but maxsize doesn't matter */
	fh_init(&fh,0);
	fh.fh_handle.fh_size = f->size;
	memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
	fh.fh_export = NULL;

	exp_readlock();
	nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp);
	fh_put(&fh);
	rqstp->rq_client = NULL;
	exp_readunlock();
 	/* nlm and nfsd don't share error codes.
	 * we invent: 0 = no error
	 *            1 = stale file handle
	 *            2 = nfserr_dropit (or -EAGAIN)
	 *	          3 = other error
	 */
	switch (nfserr) {
	case nfs_ok:
		return 0;
	case nfserr_stale:
		return 1;
	case nfserr_dropit:
		return 2;
	default:
		return 3;
	}
}
Beispiel #4
0
/*
 * Note: we hold the dentry use count while the file is open.
 */
static __be32
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
{
	__be32		nfserr;
	struct svc_fh	fh;

	/* must initialize before using! but maxsize doesn't matter */
	fh_init(&fh,0);
	fh.fh_handle.fh_size = f->size;
	memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
	fh.fh_export = NULL;

	exp_readlock();
	nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp);
	fh_put(&fh);
	exp_readunlock();
 	/* We return nlm error codes as nlm doesn't know
	 * about nfsd, but nfsd does know about nlm..
	 */
	switch (nfserr) {
	case nfs_ok:
		return 0;
	case nfserr_dropit:
		return nlm_drop_reply;
	case nfserr_stale:
		return nlm_stale_fh;
	default:
		return nlm_failed;
	}
}
Beispiel #5
0
/*
 * XDR release functions
 */
int
nfssvc_release_fhandle(struct svc_rqst *rqstp, __be32 *p,
					struct nfsd_fhandle *resp)
{
	fh_put(&resp->fh);
	return 1;
}
Beispiel #6
0
/*
 * Read a portion of a directory.
 */
static int
nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
					  struct nfsd3_readdirres  *resp)
{
	u32 *	buffer;
	int	nfserr, count;

	dprintk("nfsd: READDIR  %x/%ld %d bytes at %d\n",
				SVCFH_DEV(&argp->fh),
				SVCFH_INO(&argp->fh),
				argp->count, argp->cookie);

	/* Reserve buffer space for status */
	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);

	/* Make sure we've room for the NULL ptr & eof flag, and shrink to
	 * client read size */
	if ((count -= 8) > argp->count)
		count = argp->count;

	/* Read directory and encode entries on the fly */
	nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, 
					nfssvc_encode_entry,
					buffer, &count);
	resp->count = count;

	fh_put(&argp->fh);
	RETURN(nfserr);
}
Beispiel #7
0
/*
 * XDR release functions
 */
int
nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
					struct nfsd3_attrstat *resp)
{
	fh_put(&resp->fh);
	return 1;
}
Beispiel #8
0
/*
 * XDR release functions
 */
void
nfssvc_release_fhandle(struct svc_rqst *rqstp)
{
	struct nfsd_fhandle *resp = rqstp->rq_resp;

	fh_put(&resp->fh);
}
Beispiel #9
0
/*
 * XDR release functions
 */
static int nfs3svc_release_getacl(struct svc_rqst *rqstp, __be32 *p,
		struct nfsd3_getaclres *resp)
{
	fh_put(&resp->fh);
	posix_acl_release(resp->acl_access);
	posix_acl_release(resp->acl_default);
	return 1;
}
Beispiel #10
0
static int
nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
				void			    *resp)
{
	int	nfserr;

	dprintk("nfsd: LINK     %x/%ld -> %x/%ld %s\n",
				SVCFH_DEV(&argp->ffh),
				SVCFH_INO(&argp->ffh),
				SVCFH_DEV(&argp->tfh),
				SVCFH_INO(&argp->tfh),
				argp->tname);

	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
				  &argp->ffh);
	fh_put(&argp->ffh);
	fh_put(&argp->tfh);
	RETURN(nfserr);
}
Beispiel #11
0
static int
nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
				  	 void		        *resp)
{
	int	nfserr;

	dprintk("nfsd: RENAME   %x/%ld %s -> %x/%ld %s\n",
				SVCFH_DEV(&argp->ffh),
				SVCFH_INO(&argp->ffh),
				argp->fname,
				SVCFH_DEV(&argp->tfh),
				SVCFH_INO(&argp->tfh),
				argp->tname);

	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
				    &argp->tfh, argp->tname, argp->tlen);
	fh_put(&argp->ffh);
	fh_put(&argp->tfh);
	RETURN(nfserr);
}
Beispiel #12
0
/*
 * Get file system info
 */
static int
nfsd3_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
					  struct nfsd3_statfsres *resp)
{
	int	nfserr;

	dprintk("nfsd: STATFS   %x/%ld\n",
				SVCFH_DEV(&argp->fh),
				SVCFH_INO(&argp->fh));

	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
	fh_put(&argp->fh);
	RETURN(nfserr);
}
Beispiel #13
0
/*
 * Remove a directory
 */
static int
nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
				 	void		      *resp)
{
	int	nfserr;

	dprintk("nfsd: RMDIR    %x/%ld %s\n",
				SVCFH_DEV(&argp->fh),
				SVCFH_INO(&argp->fh),
				argp->name);

	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
	fh_put(&argp->fh);
	RETURN(nfserr);
}
Beispiel #14
0
/*
 * Make directory. This operation is not idempotent.
 * N.B. After this call resp->fh needs an fh_put
 */
static int
nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
					struct nfsd3_diropres   *resp)
{
	int	nfserr;

	dprintk("nfsd: MKDIR    %x/%ld %s\n",
				SVCFH_DEV(&argp->fh),
				SVCFH_INO(&argp->fh),
				argp->name);

	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
				    &argp->attrs, S_IFDIR, 0, &resp->fh);
	fh_put(&argp->fh);
	RETURN(nfserr);
}
Beispiel #15
0
static __be32 *encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, int namlen, u64 ino)
{
	struct svc_fh	*fh = &cd->scratch;
	__be32 err;

	fh_init(fh, NFS3_FHSIZE);
	err = compose_entry_fh(cd, fh, name, namlen, ino);
	if (err) {
		*p++ = 0;
		*p++ = 0;
		goto out;
	}
	p = encode_post_op_attr(cd->rqstp, p, fh);
	*p++ = xdr_one;			/* yes, a file handle follows */
	p = encode_fh(p, fh);
out:
	fh_put(fh);
	return p;
}
Beispiel #16
0
/*
 * Read a symlink.
 */
static int
nfsd3_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle     *argp,
					   struct nfsd3_readlinkres *resp)
{
	u32		*path;
	int		dummy, nfserr;

	dprintk("nfsd: READLINK %x/%ld\n",
				SVCFH_DEV(&argp->fh),
				SVCFH_INO(&argp->fh));

	/* Reserve room for status, post_op_attr, and path length */
	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 1 + 22 + 1);

	/* Read the symlink. */
	resp->len = NFS3_MAXPATHLEN;
	nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len);
	fh_put(&argp->fh);
	RETURN(nfserr);
}
Beispiel #17
0
/* N.B. Is nfsd3_attrstat * correct for resp?? table says "void" */
static int
nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
					  struct nfsd3_attrstat  *resp)
{
	int	nfserr;

	dprintk("nfsd: REMOVE   %x/%ld %s\n",
				SVCFH_DEV(&argp->fh),
				SVCFH_INO(&argp->fh),
				argp->name);

	/* Is this correct?? */
	fh_copy(&resp->fh, &argp->fh);

	/* Unlink. -S_IFDIR means file must not be a directory */
	nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
	/* 
	 * N.B. Should be an fh_put here ... nfsd3_proc_rmdir has one,
	 * or else as an xdr release function
	 */
	fh_put(&resp->fh);
	RETURN(nfserr);
}
Beispiel #18
0
__be32
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 and for the same filesystem, 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
	 *
	 */

	u8 version = 1;
	u8 fsid_type = 0;
	struct inode * inode = dentry->d_inode;
	struct dentry *parent = dentry->d_parent;
	__u32 *datap;
	dev_t ex_dev = exp->ex_dentry->d_inode->i_sb->s_dev;
	int root_export = (exp->ex_dentry == exp->ex_dentry->d_sb->s_root);

	dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n",
		MAJOR(ex_dev), MINOR(ex_dev),
		(long) exp->ex_dentry->d_inode->i_ino,
		parent->d_name.name, dentry->d_name.name,
		(inode ? inode->i_ino : 0));

	/* Choose filehandle version and fsid type based on
	 * the reference filehandle (if it is in the same export)
	 * or the export options.
	 */
	if (ref_fh && ref_fh->fh_export == exp) {
		version = ref_fh->fh_handle.fh_version;
		if (version == 0xca)
			fsid_type = FSID_DEV;
		else
			fsid_type = ref_fh->fh_handle.fh_fsid_type;
		/* We know this version/type works for this export
		 * so there is no need for further checks.
		 */
	} else if (exp->ex_uuid) {
		if (fhp->fh_maxsize >= 64) {
			if (root_export)
				fsid_type = FSID_UUID16;
			else
				fsid_type = FSID_UUID16_INUM;
		} else {
			if (root_export)
				fsid_type = FSID_UUID8;
			else
				fsid_type = FSID_UUID4_INUM;
		}
	} else if (exp->ex_flags & NFSEXP_FSID)
		fsid_type = FSID_NUM;
	else if (!old_valid_dev(ex_dev))
		/* for newer device numbers, we must use a newer fsid format */
		fsid_type = FSID_ENCODE_DEV;
	else
		fsid_type = FSID_DEV;

	if (ref_fh == fhp)
		fh_put(ref_fh);

	if (fhp->fh_locked || fhp->fh_dentry) {
		printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
		       parent->d_name.name, dentry->d_name.name);
	}
	if (fhp->fh_maxsize < NFS_FHSIZE)
		printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n",
		       fhp->fh_maxsize,
		       parent->d_name.name, dentry->d_name.name);

	fhp->fh_dentry = dget(dentry); /* our internal copy */
	fhp->fh_export = exp;
	cache_get(&exp->h);

	if (version == 0xca) {
		/* 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 =  old_encode_dev(ex_dev);
		fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
		fhp->fh_handle.ofh_xino =
			ino_t_to_u32(exp->ex_dentry->d_inode->i_ino);
		fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
		if (inode)
			_fh_update_old(dentry, exp, &fhp->fh_handle);
	} else {
		int len;
		fhp->fh_handle.fh_version = 1;
		fhp->fh_handle.fh_auth_type = 0;
		datap = fhp->fh_handle.fh_auth+0;
		fhp->fh_handle.fh_fsid_type = fsid_type;
		mk_fsid(fsid_type, datap, ex_dev,
			exp->ex_dentry->d_inode->i_ino,
			exp->ex_fsid, exp->ex_uuid);

		len = key_len(fsid_type);
		datap += len/4;
		fhp->fh_handle.fh_size = 4 + len;

		if (inode) {
			int size = (fhp->fh_maxsize-len-4)/4;
			fhp->fh_handle.fh_fileid_type =
				_fh_update(dentry, exp, datap, &size);
			fhp->fh_handle.fh_size += size*4;
		}
		if (fhp->fh_handle.fh_fileid_type == 255)
			return nfserr_opnotsupp;
	}

	nfsd_nr_verified++;
	return 0;
}
Beispiel #19
0
static int
encode_entry(struct readdir_cd *cd, const char *name,
	     int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus)
{
	u32		*p = cd->buffer;
	int		buflen, slen, elen;

	if (cd->offset)
		xdr_encode_hyper(cd->offset, (u64) offset);

	/* nfsd_readdir calls us with name == 0 when it wants us to
	 * set the last offset entry. */
	if (name == 0)
		return 0;

	/*
	dprintk("encode_entry(%.*s @%ld%s)\n",
		namlen, name, (long) offset, plus? " plus" : "");
	 */

	/* truncate filename if too long */
	if (namlen > NFS3_MAXNAMLEN)
		namlen = NFS3_MAXNAMLEN;

	slen = XDR_QUADLEN(namlen);
	elen = slen + NFS3_ENTRY_BAGGAGE
		+ (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
	if ((buflen = cd->buflen - elen) < 0) {
		cd->eob = 1;
		return -EINVAL;
	}
	*p++ = xdr_one;				 /* mark entry present */
	p    = xdr_encode_hyper(p, ino);	 /* file id */
	p    = xdr_encode_array(p, name, namlen);/* name length & name */

	cd->offset = p;			/* remember pointer */
	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);	/* offset of next entry */

	/* throw in readdirplus baggage */
	if (plus) {
		struct svc_fh	fh;
		struct svc_export	*exp;
		struct dentry		*dparent, *dchild;

		dparent = cd->dirfh->fh_dentry;
		exp  = cd->dirfh->fh_export;

		fh_init(&fh, NFS3_FHSIZE);
		if (isdotent(name, namlen)) {
			dchild = dparent;
			if (namlen == 2)
				dchild = dchild->d_parent;
			dchild = dget(dchild);
		} else
			dchild = lookup_one_len(name, dparent,namlen);
		if (IS_ERR(dchild))
			goto noexec;
		if (fh_compose(&fh, exp, dchild, cd->dirfh) != 0 || !dchild->d_inode)
			goto noexec;
		p = encode_post_op_attr(cd->rqstp, p, &fh);
		*p++ = xdr_one; /* yes, a file handle follows */
		p = encode_fh(p, &fh);
		fh_put(&fh);
	}

out:
	cd->buflen = buflen;
	cd->buffer = p;
	return 0;

noexec:
	*p++ = 0;
	*p++ = 0;
	goto out;
}
int
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
	 *
	 */

	u8 ref_fh_version = 0;
	u8 ref_fh_fsid_type = 0;
	struct inode * inode = dentry->d_inode;
	struct dentry *parent = dentry->d_parent;
	__u32 *datap;
	dev_t ex_dev = exp->ex_dentry->d_inode->i_sb->s_dev;

	dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n",
		MAJOR(ex_dev), MINOR(ex_dev),
		(long) exp->ex_dentry->d_inode->i_ino,
		parent->d_name.name, dentry->d_name.name,
		(inode ? inode->i_ino : 0));

	if (ref_fh) {
		ref_fh_version = ref_fh->fh_handle.fh_version;
		if (ref_fh_version == 0xca)
			ref_fh_fsid_type = 0;
		else
			ref_fh_fsid_type = ref_fh->fh_handle.fh_fsid_type;
		if (ref_fh_fsid_type > 3)
			ref_fh_fsid_type = 0;
	}
	/* make sure ref_fh type works for given export */
	if (ref_fh_fsid_type == 1 &&
	    !(exp->ex_flags & NFSEXP_FSID)) {
		/* if we don't have an fsid, we cannot provide one... */
		ref_fh_fsid_type = 0;
	}
	if (!old_valid_dev(ex_dev) && ref_fh_fsid_type == 0) {
		/* for newer device numbers, we must use a newer fsid format */
		ref_fh_version = 1;
		ref_fh_fsid_type = 3;
	}
	if (old_valid_dev(ex_dev) &&
	    (ref_fh_fsid_type == 2 || ref_fh_fsid_type == 3))
		/* must use type1 for smaller device numbers */
		ref_fh_fsid_type = 0;

	if (ref_fh == fhp)
		fh_put(ref_fh);

	if (fhp->fh_locked || fhp->fh_dentry) {
		printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
			parent->d_name.name, dentry->d_name.name);
	}
	if (fhp->fh_maxsize < NFS_FHSIZE)
		printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n",
		       fhp->fh_maxsize, parent->d_name.name, dentry->d_name.name);

	fhp->fh_dentry = dget(dentry); /* our internal copy */
	fhp->fh_export = exp;
	cache_get(&exp->h);

	if (ref_fh_version == 0xca) {
		/* 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 =  old_encode_dev(ex_dev);
		fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
		fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_dentry->d_inode->i_ino);
		fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
		if (inode)
			_fh_update_old(dentry, exp, &fhp->fh_handle);
	} else {
		int len;
		fhp->fh_handle.fh_version = 1;
		fhp->fh_handle.fh_auth_type = 0;
		datap = fhp->fh_handle.fh_auth+0;
		fhp->fh_handle.fh_fsid_type = ref_fh_fsid_type;
		switch (ref_fh_fsid_type) {
			case 0:
				/*
				 * fsid_type 0:
				 * 2byte major, 2byte minor, 4byte inode
				 */
				mk_fsid_v0(datap, ex_dev,
					exp->ex_dentry->d_inode->i_ino);
				break;
			case 1:
				/* fsid_type 1 == 4 bytes filesystem id */
				mk_fsid_v1(datap, exp->ex_fsid);
				break;
			case 2:
				/*
				 * fsid_type 2:
				 * 4byte major, 4byte minor, 4byte inode
				 */
				mk_fsid_v2(datap, ex_dev,
					exp->ex_dentry->d_inode->i_ino);
				break;
			case 3:
				/*
				 * fsid_type 3:
				 * 4byte devicenumber, 4byte inode
				 */
				mk_fsid_v3(datap, ex_dev,
					exp->ex_dentry->d_inode->i_ino);
				break;
		}
		len = key_len(ref_fh_fsid_type);
		datap += len/4;
		fhp->fh_handle.fh_size = 4 + len;

		if (inode) {
			int size = (fhp->fh_maxsize-len-4)/4;
			fhp->fh_handle.fh_fileid_type =
				_fh_update(dentry, exp, datap, &size);
			fhp->fh_handle.fh_size += size*4;
		}
		if (fhp->fh_handle.fh_fileid_type == 255)
			return nfserr_opnotsupp;
	}

	nfsd_nr_verified++;
	return 0;
}
Beispiel #21
0
static int nfsaclsvc_release_access(struct svc_rqst *rqstp, __be32 *p,
               struct nfsd3_accessres *resp)
{
       fh_put(&resp->fh);
       return 1;
}
Beispiel #22
0
static int nfsaclsvc_release_attrstat(struct svc_rqst *rqstp, __be32 *p,
		struct nfsd_attrstat *resp)
{
	fh_put(&resp->fh);
	return 1;
}
Beispiel #23
0
__be32
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 and for the same filesystem, 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
	 *
	 */

	u8 version;
	u8 fsid_type = 0;
	struct inode * inode = dentry->d_inode;
	struct dentry *parent = dentry->d_parent;
	__u32 *datap;
	dev_t ex_dev = exp->ex_path.dentry->d_inode->i_sb->s_dev;
	int root_export = (exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root);

	dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n",
		MAJOR(ex_dev), MINOR(ex_dev),
		(long) exp->ex_path.dentry->d_inode->i_ino,
		parent->d_name.name, dentry->d_name.name,
		(inode ? inode->i_ino : 0));

	/* Choose filehandle version and fsid type based on
	 * the reference filehandle (if it is in the same export)
	 * or the export options.
	 */
 retry:
	version = 1;
	if (ref_fh && ref_fh->fh_export == exp) {
		version = ref_fh->fh_handle.fh_version;
		fsid_type = ref_fh->fh_handle.fh_fsid_type;

		if (ref_fh == fhp)
			fh_put(ref_fh);
		ref_fh = NULL;

		switch (version) {
		case 0xca:
			fsid_type = FSID_DEV;
			break;
		case 1:
			break;
		default:
			goto retry;
		}

		/* Need to check that this type works for this
		 * export point.  As the fsid -> filesystem mapping
		 * was guided by user-space, there is no guarantee
		 * that the filesystem actually supports that fsid
		 * type. If it doesn't we loop around again without
		 * ref_fh set.
		 */
		switch(fsid_type) {
		case FSID_DEV:
			if (!old_valid_dev(ex_dev))
				goto retry;
			/* FALL THROUGH */
		case FSID_MAJOR_MINOR:
		case FSID_ENCODE_DEV:
			if (!(exp->ex_path.dentry->d_inode->i_sb->s_type->fs_flags
			      & FS_REQUIRES_DEV))
				goto retry;
			break;
		case FSID_NUM:
			if (! (exp->ex_flags & NFSEXP_FSID))
				goto retry;
			break;
		case FSID_UUID8:
		case FSID_UUID16:
			if (!root_export)
				goto retry;
			/* fall through */
		case FSID_UUID4_INUM:
		case FSID_UUID16_INUM:
			if (exp->ex_uuid == NULL)
				goto retry;
			break;
		}
	} else if (exp->ex_uuid) {
		if (fhp->fh_maxsize >= 64) {
			if (root_export)
				fsid_type = FSID_UUID16;
			else
				fsid_type = FSID_UUID16_INUM;
		} else {
			if (root_export)
				fsid_type = FSID_UUID8;
			else
				fsid_type = FSID_UUID4_INUM;
		}
	} else if (exp->ex_flags & NFSEXP_FSID)
		fsid_type = FSID_NUM;
	else if (!old_valid_dev(ex_dev))
		/* for newer device numbers, we must use a newer fsid format */
		fsid_type = FSID_ENCODE_DEV;
	else
		fsid_type = FSID_DEV;

	if (ref_fh == fhp)
		fh_put(ref_fh);

	if (fhp->fh_locked || fhp->fh_dentry) {
		printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
		       parent->d_name.name, dentry->d_name.name);
	}
	if (fhp->fh_maxsize < NFS_FHSIZE)
		printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n",
		       fhp->fh_maxsize,
		       parent->d_name.name, dentry->d_name.name);

	fhp->fh_dentry = dget(dentry); /* our internal copy */
	fhp->fh_export = exp;
	cache_get(&exp->h);

	if (version == 0xca) {
		/* 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 =  old_encode_dev(ex_dev);
		fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
		fhp->fh_handle.ofh_xino =
			ino_t_to_u32(exp->ex_path.dentry->d_inode->i_ino);
		fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
		if (inode)
			_fh_update_old(dentry, exp, &fhp->fh_handle);
	} else {
		int len;
		fhp->fh_handle.fh_version = 1;
		fhp->fh_handle.fh_auth_type = 0;
		datap = fhp->fh_handle.fh_auth+0;
		fhp->fh_handle.fh_fsid_type = fsid_type;
		mk_fsid(fsid_type, datap, ex_dev,
			exp->ex_path.dentry->d_inode->i_ino,
			exp->ex_fsid, exp->ex_uuid);

		len = key_len(fsid_type);
		datap += len/4;
		fhp->fh_handle.fh_size = 4 + len;

		if (inode)
			_fh_update(fhp, exp, dentry);
		if (fhp->fh_handle.fh_fileid_type == 255)
			return nfserr_opnotsupp;
	}

	nfsd_nr_verified++;
	return 0;
}