nfsfh.c 14.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * linux/fs/nfsd/nfsfh.c
 *
 * NFS server file handle treatment.
 *
 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
 * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
 * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
 * ... and again Southern-Winter 2001 to support export_operations
 */

#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/dcache.h>
18
#include <linux/exportfs.h>
L
Linus Torvalds 已提交
19 20
#include <linux/mount.h>

21
#include <linux/sunrpc/clnt.h>
L
Linus Torvalds 已提交
22
#include <linux/sunrpc/svc.h>
23
#include <linux/sunrpc/svcauth_gss.h>
L
Linus Torvalds 已提交
24
#include <linux/nfsd/nfsd.h>
25
#include "auth.h"
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

#define NFSDDBG_FACILITY		NFSDDBG_FH


static int nfsd_nr_verified;
static int nfsd_nr_put;

/*
 * our acceptability function.
 * if NOSUBTREECHECK, accept anything
 * if not, require that we can walk up to exp->ex_dentry
 * doing some checks on the 'x' bits
 */
static int nfsd_acceptable(void *expv, struct dentry *dentry)
{
	struct svc_export *exp = expv;
	int rv;
	struct dentry *tdentry;
	struct dentry *parent;

	if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
		return 1;

	tdentry = dget(dentry);
	while (tdentry != exp->ex_dentry && ! IS_ROOT(tdentry)) {
		/* make sure parents give x permission to user */
		int err;
		parent = dget_parent(tdentry);
		err = permission(parent->d_inode, MAY_EXEC, NULL);
		if (err < 0) {
			dput(parent);
			break;
		}
		dput(tdentry);
		tdentry = parent;
	}
	if (tdentry != exp->ex_dentry)
		dprintk("nfsd_acceptable failed at %p %s\n", tdentry, tdentry->d_name.name);
	rv = (tdentry == exp->ex_dentry);
	dput(tdentry);
	return rv;
}

/* Type check. The correct error return for type mismatches does not seem to be
 * generally agreed upon. SunOS seems to use EISDIR if file isn't S_IFREG; a
 * comment in the NFSv3 spec says this is incorrect (implementation notes for
 * the write call).
 */
74
static inline __be32
L
Linus Torvalds 已提交
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type)
{
	/* Type can be negative when creating hardlinks - not to a dir */
	if (type > 0 && (mode & S_IFMT) != type) {
		if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK)
			return nfserr_symlink;
		else if (type == S_IFDIR)
			return nfserr_notdir;
		else if ((mode & S_IFMT) == S_IFDIR)
			return nfserr_isdir;
		else
			return nfserr_inval;
	}
	if (type < 0 && (mode & S_IFMT) == -type) {
		if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK)
			return nfserr_symlink;
		else if (type == -S_IFDIR)
			return nfserr_isdir;
		else
			return nfserr_notdir;
	}
	return 0;
}

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
					  struct svc_export *exp)
{
	/* Check if the request originated from a secure port. */
	if (!rqstp->rq_secure && EX_SECURE(exp)) {
		char buf[RPC_MAX_ADDRBUFLEN];
		dprintk(KERN_WARNING
		       "nfsd: request from insecure port %s!\n",
		       svc_print_addr(rqstp, buf, sizeof(buf)));
		return nfserr_perm;
	}

	/* Set user creds for this exportpoint */
	return nfserrno(nfsd_setuser(rqstp, exp));
}

L
Linus Torvalds 已提交
115 116 117 118 119 120 121 122 123
/*
 * Perform sanity checks on the dentry in a client's file handle.
 *
 * Note that the file handle dentry may need to be freed even after
 * an error return.
 *
 * This is only called at the start of an nfsproc call, so fhp points to
 * a svc_fh which is all 0 except for the over-the-wire file handle.
 */
124
__be32
L
Linus Torvalds 已提交
125 126 127 128 129
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
{
	struct knfsd_fh	*fh = &fhp->fh_handle;
	struct svc_export *exp = NULL;
	struct dentry	*dentry;
130
	__be32		error = 0;
L
Linus Torvalds 已提交
131 132 133 134

	dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));

	if (!fhp->fh_dentry) {
C
Christoph Hellwig 已提交
135
		struct fid *fid = NULL, sfid;
L
Linus Torvalds 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
		int fileid_type;
		int data_left = fh->fh_size/4;

		error = nfserr_stale;
		if (rqstp->rq_vers > 2)
			error = nfserr_badhandle;
		if (rqstp->rq_vers == 4 && fh->fh_size == 0)
			return nfserr_nofilehandle;

		if (fh->fh_version == 1) {
			int len;
			if (--data_left<0) goto out;
			switch (fh->fh_auth_type) {
			case 0: break;
			default: goto out;
			}
			len = key_len(fh->fh_fsid_type) / 4;
			if (len == 0) goto out;
154
			if  (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
L
Linus Torvalds 已提交
155
				/* deprecated, convert to type 3 */
156 157
				len = key_len(FSID_ENCODE_DEV)/4;
				fh->fh_fsid_type = FSID_ENCODE_DEV;
L
Linus Torvalds 已提交
158 159 160 161
				fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1])));
				fh->fh_fsid[1] = fh->fh_fsid[2];
			}
			if ((data_left -= len)<0) goto out;
C
Christoph Hellwig 已提交
162 163 164
			exp = rqst_exp_find(rqstp, fh->fh_fsid_type,
					    fh->fh_auth);
			fid = (struct fid *)(fh->fh_auth + len);
L
Linus Torvalds 已提交
165
		} else {
C
Christoph Hellwig 已提交
166
			__u32 tfh[2];
L
Linus Torvalds 已提交
167 168 169 170 171 172 173
			dev_t xdev;
			ino_t xino;
			if (fh->fh_size != NFS_FHSIZE)
				goto out;
			/* assume old filehandle format */
			xdev = old_decode_dev(fh->ofh_xdev);
			xino = u32_to_ino_t(fh->ofh_xino);
174
			mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
175
			exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
L
Linus Torvalds 已提交
176 177
		}

178 179
		error = nfserr_stale;
		if (PTR_ERR(exp) == -ENOENT)
L
Linus Torvalds 已提交
180 181
			goto out;

182 183
		if (IS_ERR(exp)) {
			error = nfserrno(PTR_ERR(exp));
L
Linus Torvalds 已提交
184
			goto out;
185
		}
L
Linus Torvalds 已提交
186

187
		error = nfsd_setuser_and_check_port(rqstp, exp);
188 189 190
		if (error)
			goto out;

L
Linus Torvalds 已提交
191 192 193 194 195 196 197 198
		/*
		 * Look up the dentry using the NFS file handle.
		 */
		error = nfserr_stale;
		if (rqstp->rq_vers > 2)
			error = nfserr_badhandle;

		if (fh->fh_version != 1) {
C
Christoph Hellwig 已提交
199 200 201 202
			sfid.i32.ino = fh->ofh_ino;
			sfid.i32.gen = fh->ofh_generation;
			sfid.i32.parent_ino = fh->ofh_dirino;
			fid = &sfid;
L
Linus Torvalds 已提交
203 204
			data_left = 3;
			if (fh->ofh_dirino == 0)
C
Christoph Hellwig 已提交
205
				fileid_type = FILEID_INO32_GEN;
L
Linus Torvalds 已提交
206
			else
C
Christoph Hellwig 已提交
207
				fileid_type = FILEID_INO32_GEN_PARENT;
L
Linus Torvalds 已提交
208 209
		} else
			fileid_type = fh->fh_fileid_type;
210

C
Christoph Hellwig 已提交
211
		if (fileid_type == FILEID_ROOT)
L
Linus Torvalds 已提交
212 213
			dentry = dget(exp->ex_dentry);
		else {
C
Christoph Hellwig 已提交
214
			dentry = exportfs_decode_fh(exp->ex_mnt, fid,
215 216
					data_left, fileid_type,
					nfsd_acceptable, exp);
L
Linus Torvalds 已提交
217 218 219 220 221 222 223 224
		}
		if (dentry == NULL)
			goto out;
		if (IS_ERR(dentry)) {
			if (PTR_ERR(dentry) != -EINVAL)
				error = nfserrno(PTR_ERR(dentry));
			goto out;
		}
225

L
Linus Torvalds 已提交
226 227 228 229 230 231 232 233 234 235
		if (S_ISDIR(dentry->d_inode->i_mode) &&
		    (dentry->d_flags & DCACHE_DISCONNECTED)) {
			printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
			       dentry->d_parent->d_name.name, dentry->d_name.name);
		}

		fhp->fh_dentry = dentry;
		fhp->fh_export = exp;
		nfsd_nr_verified++;
	} else {
236 237 238 239
		/*
		 * just rechecking permissions
		 * (e.g. nfsproc_create calls fh_verify, then nfsd_create
		 * does as well)
L
Linus Torvalds 已提交
240 241 242 243
		 */
		dprintk("nfsd: fh_verify - just checking\n");
		dentry = fhp->fh_dentry;
		exp = fhp->fh_export;
244 245
		/*
		 * Set user creds for this exportpoint; necessary even
246 247 248
		 * in the "just checking" case because this may be a
		 * filehandle that was created by fh_compose, and that
		 * is about to be used in another nfsv4 compound
249 250 251
		 * operation.
		 */
		error = nfsd_setuser_and_check_port(rqstp, exp);
252 253
		if (error)
			goto out;
L
Linus Torvalds 已提交
254 255 256
	}
	cache_get(&exp->h);

257

L
Linus Torvalds 已提交
258 259 260 261
	error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
	if (error)
		goto out;

262 263 264 265 266 267 268 269 270 271
	if (!(access & MAY_LOCK)) {
		/*
		 * pseudoflavor restrictions are not enforced on NLM,
		 * which clients virtually always use auth_sys for,
		 * even while using RPCSEC_GSS for NFS.
		 */
		error = check_nfsd_access(exp, rqstp);
		if (error)
			goto out;
	}
272

L
Linus Torvalds 已提交
273
	/* Finally, check access permissions. */
274
	error = nfsd_permission(rqstp, exp, dentry, access);
L
Linus Torvalds 已提交
275 276

	if (error) {
277 278 279 280
		dprintk("fh_verify: %s/%s permission failure, "
			"acc=%x, error=%d\n",
			dentry->d_parent->d_name.name,
			dentry->d_name.name,
281
			access, ntohl(error));
L
Linus Torvalds 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
	}
out:
	if (exp && !IS_ERR(exp))
		exp_put(exp);
	if (error == nfserr_stale)
		nfsdstats.fh_stale++;
	return error;
}


/*
 * Compose a file handle for an NFS reply.
 *
 * Note that when first composed, the dentry may not yet have
 * an inode.  In this case a call to fh_update should be made
 * before the fh goes out on the wire ...
 */
C
Christoph Hellwig 已提交
299 300
static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
		struct dentry *dentry)
L
Linus Torvalds 已提交
301
{
C
Christoph Hellwig 已提交
302 303 304 305 306
	if (dentry != exp->ex_dentry) {
		struct fid *fid = (struct fid *)
			(fhp->fh_handle.fh_auth + fhp->fh_handle.fh_size/4 - 1);
		int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
		int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
L
Linus Torvalds 已提交
307

C
Christoph Hellwig 已提交
308 309 310 311 312 313
		fhp->fh_handle.fh_fileid_type =
			exportfs_encode_fh(dentry, fid, &maxsize, subtreecheck);
		fhp->fh_handle.fh_size += maxsize * 4;
	} else {
		fhp->fh_handle.fh_fileid_type = FILEID_ROOT;
	}
L
Linus Torvalds 已提交
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
}

/*
 * for composing old style file handles
 */
static 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;
}

330
__be32
331 332
fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
	   struct svc_fh *ref_fh)
L
Linus Torvalds 已提交
333 334
{
	/* ref_fh is a reference file handle.
335 336
	 * if it is non-null and for the same filesystem, then we should compose
	 * a filehandle which is of the same version, where possible.
L
Linus Torvalds 已提交
337 338 339 340 341
	 * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
	 * Then create a 32byte filehandle using nfs_fhbase_old
	 *
	 */

342
	u8 version;
343
	u8 fsid_type = 0;
L
Linus Torvalds 已提交
344 345 346 347
	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;
348
	int root_export = (exp->ex_dentry == exp->ex_dentry->d_sb->s_root);
L
Linus Torvalds 已提交
349 350 351 352 353 354 355

	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));

356 357 358 359
	/* Choose filehandle version and fsid type based on
	 * the reference filehandle (if it is in the same export)
	 * or the export options.
	 */
360 361
 retry:
	version = 1;
362
	if (ref_fh && ref_fh->fh_export == exp) {
363
		version = ref_fh->fh_handle.fh_version;
364 365 366 367 368 369 370 371
		fsid_type = ref_fh->fh_handle.fh_fsid_type;

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

		switch (version) {
		case 0xca:
372
			fsid_type = FSID_DEV;
373 374 375 376 377 378 379 380 381 382 383 384 385
			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.
386
		 */
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
		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_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;
		}
413 414 415 416 417 418 419 420 421 422 423 424
	} 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;
		}
L
Linus Torvalds 已提交
425
	} else if (exp->ex_flags & NFSEXP_FSID)
426
		fsid_type = FSID_NUM;
427
	else if (!old_valid_dev(ex_dev))
L
Linus Torvalds 已提交
428
		/* for newer device numbers, we must use a newer fsid format */
429
		fsid_type = FSID_ENCODE_DEV;
430
	else
431
		fsid_type = FSID_DEV;
L
Linus Torvalds 已提交
432 433 434 435 436 437

	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",
438
		       parent->d_name.name, dentry->d_name.name);
L
Linus Torvalds 已提交
439 440 441
	}
	if (fhp->fh_maxsize < NFS_FHSIZE)
		printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n",
442 443
		       fhp->fh_maxsize,
		       parent->d_name.name, dentry->d_name.name);
L
Linus Torvalds 已提交
444 445 446 447 448

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

449
	if (version == 0xca) {
L
Linus Torvalds 已提交
450 451 452 453 454 455
		/* 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;
456 457
		fhp->fh_handle.ofh_xino =
			ino_t_to_u32(exp->ex_dentry->d_inode->i_ino);
L
Linus Torvalds 已提交
458 459 460 461 462 463 464 465
		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;
466
		fhp->fh_handle.fh_fsid_type = fsid_type;
467 468 469 470
		mk_fsid(fsid_type, datap, ex_dev,
			exp->ex_dentry->d_inode->i_ino,
			exp->ex_fsid, exp->ex_uuid);

471
		len = key_len(fsid_type);
L
Linus Torvalds 已提交
472 473 474
		datap += len/4;
		fhp->fh_handle.fh_size = 4 + len;

C
Christoph Hellwig 已提交
475 476
		if (inode)
			_fh_update(fhp, exp, dentry);
L
Linus Torvalds 已提交
477 478 479 480 481 482 483 484 485 486 487 488
		if (fhp->fh_handle.fh_fileid_type == 255)
			return nfserr_opnotsupp;
	}

	nfsd_nr_verified++;
	return 0;
}

/*
 * Update file handle information after changing a dentry.
 * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create
 */
489
__be32
L
Linus Torvalds 已提交
490 491 492
fh_update(struct svc_fh *fhp)
{
	struct dentry *dentry;
493

L
Linus Torvalds 已提交
494 495 496 497 498 499 500 501 502
	if (!fhp->fh_dentry)
		goto out_bad;

	dentry = fhp->fh_dentry;
	if (!dentry->d_inode)
		goto out_negative;
	if (fhp->fh_handle.fh_version != 1) {
		_fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
	} else {
C
Christoph Hellwig 已提交
503
		if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT)
504
			goto out;
C
Christoph Hellwig 已提交
505 506

		_fh_update(fhp, fhp->fh_export, dentry);
L
Linus Torvalds 已提交
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
		if (fhp->fh_handle.fh_fileid_type == 255)
			return nfserr_opnotsupp;
	}
out:
	return 0;

out_bad:
	printk(KERN_ERR "fh_update: fh not verified!\n");
	goto out;
out_negative:
	printk(KERN_ERR "fh_update: %s/%s still negative!\n",
		dentry->d_parent->d_name.name, dentry->d_name.name);
	goto out;
}

/*
 * Release a file handle.
 */
void
fh_put(struct svc_fh *fhp)
{
	struct dentry * dentry = fhp->fh_dentry;
	struct svc_export * exp = fhp->fh_export;
	if (dentry) {
		fh_unlock(fhp);
		fhp->fh_dentry = NULL;
		dput(dentry);
#ifdef CONFIG_NFSD_V3
		fhp->fh_pre_saved = 0;
		fhp->fh_post_saved = 0;
#endif
		nfsd_nr_put++;
	}
	if (exp) {
541
		cache_put(&exp->h, &svc_export_cache);
L
Linus Torvalds 已提交
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
		fhp->fh_export = NULL;
	}
	return;
}

/*
 * Shorthand for dprintk()'s
 */
char * SVCFH_fmt(struct svc_fh *fhp)
{
	struct knfsd_fh *fh = &fhp->fh_handle;

	static char buf[80];
	sprintf(buf, "%d: %08x %08x %08x %08x %08x %08x",
		fh->fh_size,
		fh->fh_base.fh_pad[0],
		fh->fh_base.fh_pad[1],
		fh->fh_base.fh_pad[2],
		fh->fh_base.fh_pad[3],
		fh->fh_base.fh_pad[4],
		fh->fh_base.fh_pad[5]);
	return buf;
}
565 566 567 568 569 570 571 572 573

enum fsid_source fsid_source(struct svc_fh *fhp)
{
	if (fhp->fh_handle.fh_version != 1)
		return FSIDSOURCE_DEV;
	switch(fhp->fh_handle.fh_fsid_type) {
	case FSID_DEV:
	case FSID_ENCODE_DEV:
	case FSID_MAJOR_MINOR:
574 575 576 577
		if (fhp->fh_export->ex_dentry->d_inode->i_sb->s_type->fs_flags
		    & FS_REQUIRES_DEV)
			return FSIDSOURCE_DEV;
		break;
578 579 580
	case FSID_NUM:
		if (fhp->fh_export->ex_flags & NFSEXP_FSID)
			return FSIDSOURCE_FSID;
581 582 583
		break;
	default:
		break;
584
	}
585 586 587 588 589 590 591 592
	/* either a UUID type filehandle, or the filehandle doesn't
	 * match the export.
	 */
	if (fhp->fh_export->ex_flags & NFSEXP_FSID)
		return FSIDSOURCE_FSID;
	if (fhp->fh_export->ex_uuid)
		return FSIDSOURCE_UUID;
	return FSIDSOURCE_DEV;
593
}