Blame view

fs/nfsd/nfsfh.c 18.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
   * 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
   */
a56942551   Christoph Hellwig   knfsd: exportfs: ...
9
  #include <linux/exportfs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10

32c1eb0cd   Andy Adamson   knfsd: nfsd4: ret...
11
  #include <linux/sunrpc/svcauth_gss.h>
9a74af213   Boaz Harrosh   nfsd: Move privat...
12
  #include "nfsd.h"
0a3adadee   J. Bruce Fields   nfsd: make fs/nfs...
13
  #include "vfs.h"
2e8138a27   J. Bruce Fields   nfsd: move nfsd/a...
14
  #include "auth.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  
  #define NFSDDBG_FACILITY		NFSDDBG_FH
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  /*
   * 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);
547754916   Jan Blunck   Use struct path i...
35
  	while (tdentry != exp->ex_path.dentry && !IS_ROOT(tdentry)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
  		/* make sure parents give x permission to user */
  		int err;
  		parent = dget_parent(tdentry);
2b0143b5c   David Howells   VFS: normal files...
39
  		err = inode_permission(d_inode(parent), MAY_EXEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
45
46
  		if (err < 0) {
  			dput(parent);
  			break;
  		}
  		dput(tdentry);
  		tdentry = parent;
  	}
547754916   Jan Blunck   Use struct path i...
47
  	if (tdentry != exp->ex_path.dentry)
97e47fa11   Al Viro   nfsd: switch to %...
48
49
  		dprintk("nfsd_acceptable failed at %p %pd
  ", tdentry, tdentry);
547754916   Jan Blunck   Use struct path i...
50
  	rv = (tdentry == exp->ex_path.dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
58
59
  	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).
   */
83b11340d   Al Viro   [PATCH] nfsfh sim...
60
  static inline __be32
e75b23f9e   J. Bruce Fields   nfsd: check d_can...
61
62
  nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
  		umode_t requested)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  {
e75b23f9e   J. Bruce Fields   nfsd: check d_can...
64
  	umode_t mode = d_inode(dentry)->i_mode & S_IFMT;
e10f9e141   J. Bruce Fields   nfsd: clean up nf...
65
66
67
  
  	if (requested == 0) /* the caller doesn't care */
  		return nfs_ok;
e75b23f9e   J. Bruce Fields   nfsd: check d_can...
68
69
70
71
72
  	if (mode == requested) {
  		if (mode == S_IFDIR && !d_can_lookup(dentry)) {
  			WARN_ON_ONCE(1);
  			return nfserr_notdir;
  		}
e10f9e141   J. Bruce Fields   nfsd: clean up nf...
73
  		return nfs_ok;
e75b23f9e   J. Bruce Fields   nfsd: check d_can...
74
  	}
e10f9e141   J. Bruce Fields   nfsd: clean up nf...
75
76
77
78
79
80
81
82
83
84
85
  	/*
  	 * v4 has an error more specific than err_notdir which we should
  	 * return in preference to err_notdir:
  	 */
  	if (rqstp->rq_vers == 4 && mode == S_IFLNK)
  		return nfserr_symlink;
  	if (requested == S_IFDIR)
  		return nfserr_notdir;
  	if (mode == S_IFDIR)
  		return nfserr_isdir;
  	return nfserr_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  }
6fa02839b   J. Bruce Fields   nfsd4: recheck fo...
87
88
89
  static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
  					  struct svc_export *exp)
  {
12045a6ee   J. Bruce Fields   nfsd: let "insecu...
90
  	int flags = nfsexp_flags(rqstp, exp);
6fa02839b   J. Bruce Fields   nfsd4: recheck fo...
91
  	/* Check if the request originated from a secure port. */
4d152e2c9   Jeff Layton   sunrpc: add a gen...
92
  	if (!test_bit(RQ_SECURE, &rqstp->rq_flags) && !(flags & NFSEXP_INSECURE_PORT)) {
5216a8e70   Pavel Emelyanov   Wrap buffers used...
93
  		RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
a48fd0f9f   Kinglong Mee   SUNRPC/NFSD: Remo...
94
95
96
  		dprintk("nfsd: request from insecure port %s!
  ",
  		        svc_print_addr(rqstp, buf, sizeof(buf)));
6fa02839b   J. Bruce Fields   nfsd4: recheck fo...
97
98
99
100
101
102
  		return nfserr_perm;
  	}
  
  	/* Set user creds for this exportpoint */
  	return nfserrno(nfsd_setuser(rqstp, exp));
  }
03a816b46   Steve Dickson   nfsd: restrict fi...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
  	struct dentry *dentry, struct svc_export *exp)
  {
  	if (!(exp->ex_flags & NFSEXP_V4ROOT))
  		return nfs_ok;
  	/*
  	 * v2/v3 clients have no need for the V4ROOT export--they use
  	 * the mount protocl instead; also, further V4ROOT checks may be
  	 * in v4-specific code, in which case v2/v3 clients could bypass
  	 * them.
  	 */
  	if (!nfsd_v4client(rqstp))
  		return nfserr_stale;
  	/*
  	 * We're exposing only the directories and symlinks that have to be
  	 * traversed on the way to real exports:
  	 */
e36cb0b89   David Howells   VFS: (Scripted) C...
120
121
  	if (unlikely(!d_is_dir(dentry) &&
  		     !d_is_symlink(dentry)))
03a816b46   Steve Dickson   nfsd: restrict fi...
122
123
124
125
126
127
128
129
130
131
  		return nfserr_stale;
  	/*
  	 * A pseudoroot export gives permission to access only one
  	 * single directory; the kernel has to make another upcall
  	 * before granting access to anything else under it:
  	 */
  	if (unlikely(dentry != exp->ex_path.dentry))
  		return nfserr_stale;
  	return nfs_ok;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  /*
03550fac0   J. Bruce Fields   nfsd: move most o...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
   * Use the given filehandle to look up the corresponding export and
   * dentry.  On success, the results are used to set fh_export and
   * fh_dentry.
   */
  static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
  {
  	struct knfsd_fh	*fh = &fhp->fh_handle;
  	struct fid *fid = NULL, sfid;
  	struct svc_export *exp;
  	struct dentry *dentry;
  	int fileid_type;
  	int data_left = fh->fh_size/4;
  	__be32 error;
  
  	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)
  			return error;
  		if (fh->fh_auth_type != 0)
  			return error;
  		len = key_len(fh->fh_fsid_type) / 4;
  		if (len == 0)
  			return error;
  		if  (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
  			/* deprecated, convert to type 3 */
  			len = key_len(FSID_ENCODE_DEV)/4;
  			fh->fh_fsid_type = FSID_ENCODE_DEV;
94ec938b6   Jeff Layton   nfsd: add appropr...
167
168
169
170
171
172
173
174
  			/*
  			 * struct knfsd_fh uses host-endian fields, which are
  			 * sometimes used to hold net-endian values. This
  			 * confuses sparse, so we must use __force here to
  			 * keep it from complaining.
  			 */
  			fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl((__force __be32)fh->fh_fsid[0]),
  							ntohl((__force __be32)fh->fh_fsid[1])));
03550fac0   J. Bruce Fields   nfsd: move most o...
175
176
177
178
179
  			fh->fh_fsid[1] = fh->fh_fsid[2];
  		}
  		data_left -= len;
  		if (data_left < 0)
  			return error;
5409e46f1   Christoph Hellwig   nfsd: clean up fh...
180
181
  		exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid);
  		fid = (struct fid *)(fh->fh_fsid + len);
03550fac0   J. Bruce Fields   nfsd: move most o...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
  	} else {
  		__u32 tfh[2];
  		dev_t xdev;
  		ino_t xino;
  
  		if (fh->fh_size != NFS_FHSIZE)
  			return error;
  		/* assume old filehandle format */
  		xdev = old_decode_dev(fh->ofh_xdev);
  		xino = u32_to_ino_t(fh->ofh_xino);
  		mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
  		exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
  	}
  
  	error = nfserr_stale;
  	if (PTR_ERR(exp) == -ENOENT)
  		return error;
  
  	if (IS_ERR(exp))
  		return nfserrno(PTR_ERR(exp));
496d6c32d   Neil Brown   nfsd: fix spuriou...
202
203
204
205
206
207
208
209
210
211
  	if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) {
  		/* Elevate privileges so that the lack of 'r' or 'x'
  		 * permission on some parent directory will
  		 * not stop exportfs_decode_fh from being able
  		 * to reconnect a directory into the dentry cache.
  		 * The same problem can affect "SUBTREECHECK" exports,
  		 * but as nfsd_acceptable depends on correct
  		 * access control settings being in effect, we cannot
  		 * fix that case easily.
  		 */
d84f4f992   David Howells   CRED: Inaugurate ...
212
  		struct cred *new = prepare_creds();
027bc41a3   Kinglong Mee   NFSD: Put export ...
213
214
215
216
  		if (!new) {
  			error =  nfserrno(-ENOMEM);
  			goto out;
  		}
d84f4f992   David Howells   CRED: Inaugurate ...
217
218
219
220
221
  		new->cap_effective =
  			cap_raise_nfsd_set(new->cap_effective,
  					   new->cap_permitted);
  		put_cred(override_creds(new));
  		put_cred(new);
496d6c32d   Neil Brown   nfsd: fix spuriou...
222
223
224
225
226
  	} else {
  		error = nfsd_setuser_and_check_port(rqstp, exp);
  		if (error)
  			goto out;
  	}
03550fac0   J. Bruce Fields   nfsd: move most o...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  
  	/*
  	 * 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) {
  		sfid.i32.ino = fh->ofh_ino;
  		sfid.i32.gen = fh->ofh_generation;
  		sfid.i32.parent_ino = fh->ofh_dirino;
  		fid = &sfid;
  		data_left = 3;
  		if (fh->ofh_dirino == 0)
  			fileid_type = FILEID_INO32_GEN;
  		else
  			fileid_type = FILEID_INO32_GEN_PARENT;
  	} else
  		fileid_type = fh->fh_fileid_type;
  
  	if (fileid_type == FILEID_ROOT)
  		dentry = dget(exp->ex_path.dentry);
  	else {
  		dentry = exportfs_decode_fh(exp->ex_path.mnt, fid,
  				data_left, fileid_type,
  				nfsd_acceptable, exp);
  	}
  	if (dentry == NULL)
  		goto out;
  	if (IS_ERR(dentry)) {
  		if (PTR_ERR(dentry) != -EINVAL)
  			error = nfserrno(PTR_ERR(dentry));
  		goto out;
  	}
e36cb0b89   David Howells   VFS: (Scripted) C...
262
  	if (d_is_dir(dentry) &&
03550fac0   J. Bruce Fields   nfsd: move most o...
263
  			(dentry->d_flags & DCACHE_DISCONNECTED)) {
97e47fa11   Al Viro   nfsd: switch to %...
264
265
266
  		printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %pd2
  ",
  				dentry);
03550fac0   J. Bruce Fields   nfsd: move most o...
267
268
269
270
  	}
  
  	fhp->fh_dentry = dentry;
  	fhp->fh_export = exp;
03550fac0   J. Bruce Fields   nfsd: move most o...
271
272
273
274
275
  	return 0;
  out:
  	exp_put(exp);
  	return error;
  }
b3d47676d   J. Bruce Fields   nfsd: update fh_v...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
  /**
   * fh_verify - filehandle lookup and access checking
   * @rqstp: pointer to current rpc request
   * @fhp: filehandle to be verified
   * @type: expected type of object pointed to by filehandle
   * @access: type of access needed to object
   *
   * Look up a dentry from the on-the-wire filehandle, check the client's
   * access to the export, and set the current task's credentials.
   *
   * Regardless of success or failure of fh_verify(), fh_put() should be
   * called on @fhp when the caller is finished with the filehandle.
   *
   * fh_verify() may be called multiple times on a given filehandle, for
   * example, when processing an NFSv4 compound.  The first call will look
   * up a dentry using the on-the-wire filehandle.  Subsequent calls will
   * skip the lookup and just perform the other checks and possibly change
   * the current task's credentials.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
   *
b3d47676d   J. Bruce Fields   nfsd: update fh_v...
295
296
297
298
   * @type specifies the type of object expected using one of the S_IF*
   * constants defined in include/linux/stat.h.  The caller may use zero
   * to indicate that it doesn't care, or a negative integer to indicate
   * that it expects something not of the given type.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
   *
b3d47676d   J. Bruce Fields   nfsd: update fh_v...
300
   * @access is formed from the NFSD_MAY_* constants defined in
93f580a9a   Oleg Drokin   nfsd: Correct a c...
301
   * fs/nfsd/vfs.h.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
   */
83b11340d   Al Viro   [PATCH] nfsfh sim...
303
  __be32
175a4eb7e   Al Viro   fs: propagate umo...
304
  fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  {
03550fac0   J. Bruce Fields   nfsd: move most o...
306
  	struct svc_export *exp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  	struct dentry	*dentry;
03550fac0   J. Bruce Fields   nfsd: move most o...
308
  	__be32		error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
  
  	dprintk("nfsd: fh_verify(%s)
  ", SVCFH_fmt(fhp));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  	if (!fhp->fh_dentry) {
03550fac0   J. Bruce Fields   nfsd: move most o...
313
  		error = nfsd_set_fh_dentry(rqstp, fhp);
d1bbf14f3   NeilBrown   [PATCH] knfsd: Fi...
314
315
  		if (error)
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  	}
864f0f61f   J. Bruce Fields   nfsd: simplify fh...
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
  	dentry = fhp->fh_dentry;
  	exp = fhp->fh_export;
  	/*
  	 * We still have to do all these permission checks, even when
  	 * fh_dentry is already set:
  	 * 	- fh_verify may be called multiple times with different
  	 * 	  "access" arguments (e.g. nfsd_proc_create calls
  	 * 	  fh_verify(...,NFSD_MAY_EXEC) first, then later (in
  	 * 	  nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE).
  	 *	- in the NFSv4 case, the filehandle may have been filled
  	 *	  in by fh_compose, and given a dentry, but further
  	 *	  compound operations performed with that filehandle
  	 *	  still need permissions checks.  In the worst case, a
  	 *	  mountpoint crossing may have changed the export
  	 *	  options, and we may now need to use a different uid
  	 *	  (for example, if different id-squashing options are in
  	 *	  effect on the new filesystem).
  	 */
03a816b46   Steve Dickson   nfsd: restrict fi...
335
336
337
  	error = check_pseudo_root(rqstp, dentry, exp);
  	if (error)
  		goto out;
864f0f61f   J. Bruce Fields   nfsd: simplify fh...
338
339
340
  	error = nfsd_setuser_and_check_port(rqstp, exp);
  	if (error)
  		goto out;
7fc90ec93   J. Bruce Fields   [PATCH] knfsd: nf...
341

e75b23f9e   J. Bruce Fields   nfsd: check d_can...
342
  	error = nfsd_mode_check(rqstp, dentry, type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
  	if (error)
  		goto out;
04716e662   J. Bruce Fields   nfsd: permit unau...
345
346
347
348
349
  	/*
  	 * pseudoflavor restrictions are not enforced on NLM,
  	 * which clients virtually always use auth_sys for,
  	 * even while using RPCSEC_GSS for NFS.
  	 */
204f4ce75   J. Bruce Fields   nfsd4: allow fh_v...
350
  	if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
04716e662   J. Bruce Fields   nfsd: permit unau...
351
352
353
354
355
356
357
358
359
360
361
362
363
  		goto skip_pseudoflavor_check;
  	/*
  	 * Clients may expect to be able to use auth_sys during mount,
  	 * even if they use gss for everything else; see section 2.3.2
  	 * of rfc 2623.
  	 */
  	if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
  			&& exp->ex_path.dentry == dentry)
  		goto skip_pseudoflavor_check;
  
  	error = check_nfsd_access(exp, rqstp);
  	if (error)
  		goto out;
32c1eb0cd   Andy Adamson   knfsd: nfsd4: ret...
364

04716e662   J. Bruce Fields   nfsd: permit unau...
365
  skip_pseudoflavor_check:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  	/* Finally, check access permissions. */
0ec757df9   J. Bruce Fields   knfsd: nfsd4: mak...
367
  	error = nfsd_permission(rqstp, exp, dentry, access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
  	if (error) {
97e47fa11   Al Viro   nfsd: switch to %...
370
  		dprintk("fh_verify: %pd2 permission failure, "
34e9a63b4   NeilBrown   [PATCH] knfsd: ra...
371
372
  			"acc=%x, error=%d
  ",
97e47fa11   Al Viro   nfsd: switch to %...
373
  			dentry,
fc2dd2e51   Al Viro   [PATCH] endiannes...
374
  			access, ntohl(error));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
383
384
385
386
387
388
389
  	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 ...
   */
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
390
391
  static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
  		struct dentry *dentry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  {
547754916   Jan Blunck   Use struct path i...
393
  	if (dentry != exp->ex_path.dentry) {
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
394
  		struct fid *fid = (struct fid *)
5409e46f1   Christoph Hellwig   nfsd: clean up fh...
395
  			(fhp->fh_handle.fh_fsid + fhp->fh_handle.fh_size/4 - 1);
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
396
397
  		int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
  		int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398

6e91ea2bb   Christoph Hellwig   exportfs: add fid...
399
400
401
402
403
404
  		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;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
411
412
413
  }
  
  /*
   * for composing old style file handles
   */
  static inline void _fh_update_old(struct dentry *dentry,
  				  struct svc_export *exp,
  				  struct knfsd_fh *fh)
  {
2b0143b5c   David Howells   VFS: normal files...
414
415
  	fh->ofh_ino = ino_t_to_u32(d_inode(dentry)->i_ino);
  	fh->ofh_generation = d_inode(dentry)->i_generation;
e36cb0b89   David Howells   VFS: (Scripted) C...
416
  	if (d_is_dir(dentry) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
  	    (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
  		fh->ofh_dirino = 0;
  }
8e498751f   J. Bruce Fields   nfsd: move some o...
420
421
422
423
424
425
426
  static bool is_root_export(struct svc_export *exp)
  {
  	return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root;
  }
  
  static struct super_block *exp_sb(struct svc_export *exp)
  {
fc64005c9   Al Viro   don't bother with...
427
  	return exp->ex_path.dentry->d_sb;
8e498751f   J. Bruce Fields   nfsd: move some o...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  }
  
  static bool fsid_type_ok_for_exp(u8 fsid_type, struct svc_export *exp)
  {
  	switch (fsid_type) {
  	case FSID_DEV:
  		if (!old_valid_dev(exp_sb(exp)->s_dev))
  			return 0;
  		/* FALL THROUGH */
  	case FSID_MAJOR_MINOR:
  	case FSID_ENCODE_DEV:
  		return exp_sb(exp)->s_type->fs_flags & FS_REQUIRES_DEV;
  	case FSID_NUM:
  		return exp->ex_flags & NFSEXP_FSID;
  	case FSID_UUID8:
  	case FSID_UUID16:
  		if (!is_root_export(exp))
  			return 0;
  		/* fall through */
  	case FSID_UUID4_INUM:
  	case FSID_UUID16_INUM:
  		return exp->ex_uuid != NULL;
  	}
  	return 1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453

bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
454
455
  static void set_version_and_fsid_type(struct svc_fh *fhp, struct svc_export *exp, struct svc_fh *ref_fh)
  {
b41eeef14   NeilBrown   knfsd: avoid Oops...
456
  	u8 version;
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
457
458
  	u8 fsid_type;
  retry:
b41eeef14   NeilBrown   knfsd: avoid Oops...
459
  	version = 1;
7e4053645   NeilBrown   [PATCH] knfsd: ig...
460
  	if (ref_fh && ref_fh->fh_export == exp) {
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
461
  		version = ref_fh->fh_handle.fh_version;
b41eeef14   NeilBrown   knfsd: avoid Oops...
462
  		fsid_type = ref_fh->fh_handle.fh_fsid_type;
b41eeef14   NeilBrown   knfsd: avoid Oops...
463
464
465
466
  		ref_fh = NULL;
  
  		switch (version) {
  		case 0xca:
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
467
  			fsid_type = FSID_DEV;
b41eeef14   NeilBrown   knfsd: avoid Oops...
468
469
470
471
472
473
  			break;
  		case 1:
  			break;
  		default:
  			goto retry;
  		}
8e498751f   J. Bruce Fields   nfsd: move some o...
474
475
476
477
478
  		/*
  		 * 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.
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
479
  		 */
8e498751f   J. Bruce Fields   nfsd: move some o...
480
481
  		if (!fsid_type_ok_for_exp(fsid_type, exp))
  			goto retry;
30fa8c015   Steve Dickson   NFSD: FIDs need t...
482
483
  	} else if (exp->ex_flags & NFSEXP_FSID) {
  		fsid_type = FSID_NUM;
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
484
485
  	} else if (exp->ex_uuid) {
  		if (fhp->fh_maxsize >= 64) {
8e498751f   J. Bruce Fields   nfsd: move some o...
486
  			if (is_root_export(exp))
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
487
488
489
490
  				fsid_type = FSID_UUID16;
  			else
  				fsid_type = FSID_UUID16_INUM;
  		} else {
8e498751f   J. Bruce Fields   nfsd: move some o...
491
  			if (is_root_export(exp))
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
492
493
494
495
  				fsid_type = FSID_UUID8;
  			else
  				fsid_type = FSID_UUID4_INUM;
  		}
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
496
  	} else if (!old_valid_dev(exp_sb(exp)->s_dev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  		/* for newer device numbers, we must use a newer fsid format */
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
498
  		fsid_type = FSID_ENCODE_DEV;
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
499
  	else
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
500
  		fsid_type = FSID_DEV;
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
  	fhp->fh_handle.fh_version = version;
  	if (version)
  		fhp->fh_handle.fh_fsid_type = fsid_type;
  }
  
  __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
  	 *
  	 */
2b0143b5c   David Howells   VFS: normal files...
517
  	struct inode * inode = d_inode(dentry);
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
518
  	dev_t ex_dev = exp_sb(exp)->s_dev;
97e47fa11   Al Viro   nfsd: switch to %...
519
520
  	dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %pd2, ino=%ld)
  ",
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
521
  		MAJOR(ex_dev), MINOR(ex_dev),
2b0143b5c   David Howells   VFS: normal files...
522
  		(long) d_inode(exp->ex_path.dentry)->i_ino,
97e47fa11   Al Viro   nfsd: switch to %...
523
  		dentry,
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
524
525
526
527
528
529
  		(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.
  	 */
d28c442f5   Christophe JAILLET   nfsd: Fix some in...
530
  	set_version_and_fsid_type(fhp, exp, ref_fh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
535
  
  	if (ref_fh == fhp)
  		fh_put(ref_fh);
  
  	if (fhp->fh_locked || fhp->fh_dentry) {
97e47fa11   Al Viro   nfsd: switch to %...
536
537
538
  		printk(KERN_ERR "fh_compose: fh %pd2 not initialized!
  ",
  		       dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539
540
  	}
  	if (fhp->fh_maxsize < NFS_FHSIZE)
97e47fa11   Al Viro   nfsd: switch to %...
541
542
  		printk(KERN_ERR "fh_compose: called with maxsize %d! %pd2
  ",
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
543
  		       fhp->fh_maxsize,
97e47fa11   Al Viro   nfsd: switch to %...
544
  		       dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
  
  	fhp->fh_dentry = dget(dentry); /* our internal copy */
bf18f163e   Kinglong Mee   NFSD: Using exp_g...
547
  	fhp->fh_export = exp_get(exp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548

bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
549
  	if (fhp->fh_handle.fh_version == 0xca) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
552
553
554
555
  		/* 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;
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
556
  		fhp->fh_handle.ofh_xino =
2b0143b5c   David Howells   VFS: normal files...
557
  			ino_t_to_u32(d_inode(exp->ex_path.dentry)->i_ino);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
  		fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
  		if (inode)
  			_fh_update_old(dentry, exp, &fhp->fh_handle);
  	} else {
5409e46f1   Christoph Hellwig   nfsd: clean up fh...
562
563
  		fhp->fh_handle.fh_size =
  			key_len(fhp->fh_handle.fh_fsid_type) + 4;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
  		fhp->fh_handle.fh_auth_type = 0;
5409e46f1   Christoph Hellwig   nfsd: clean up fh...
565
566
567
568
  
  		mk_fsid(fhp->fh_handle.fh_fsid_type,
  			fhp->fh_handle.fh_fsid,
  			ex_dev,
2b0143b5c   David Howells   VFS: normal files...
569
  			d_inode(exp->ex_path.dentry)->i_ino,
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
570
  			exp->ex_fsid, exp->ex_uuid);
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
571
572
  		if (inode)
  			_fh_update(fhp, exp, dentry);
216b6cbdc   Namjae Jeon   exportfs: add FIL...
573
  		if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) {
1be10a88c   J. Bruce Fields   nfsd4: filehandle...
574
  			fh_put(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
  			return nfserr_opnotsupp;
1be10a88c   J. Bruce Fields   nfsd4: filehandle...
576
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
580
581
582
583
584
  	return 0;
  }
  
  /*
   * Update file handle information after changing a dentry.
   * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create
   */
83b11340d   Al Viro   [PATCH] nfsfh sim...
585
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
  fh_update(struct svc_fh *fhp)
  {
  	struct dentry *dentry;
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
589

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
  	if (!fhp->fh_dentry)
  		goto out_bad;
  
  	dentry = fhp->fh_dentry;
2b0143b5c   David Howells   VFS: normal files...
594
  	if (d_really_is_negative(dentry))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
  		goto out_negative;
  	if (fhp->fh_handle.fh_version != 1) {
  		_fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
  	} else {
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
599
  		if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT)
49e737206   J. Bruce Fields   nfsd: fh_update s...
600
  			return 0;
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
601
602
  
  		_fh_update(fhp, fhp->fh_export, dentry);
216b6cbdc   Namjae Jeon   exportfs: add FIL...
603
  		if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
  			return nfserr_opnotsupp;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
609
  out_bad:
  	printk(KERN_ERR "fh_update: fh not verified!
  ");
49e737206   J. Bruce Fields   nfsd: fh_update s...
610
  	return nfserr_serverfault;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  out_negative:
97e47fa11   Al Viro   nfsd: switch to %...
612
613
614
  	printk(KERN_ERR "fh_update: %pd2 still negative!
  ",
  		dentry);
49e737206   J. Bruce Fields   nfsd: fh_update s...
615
  	return nfserr_serverfault;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
618
619
620
621
622
623
624
625
626
627
628
629
  }
  
  /*
   * 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);
aaf91ec14   Jeff Layton   nfsd: switch unsi...
630
  		fh_clear_wcc(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
  	}
4a55c1017   Jan Kara   nfsd: Push mnt_wa...
632
  	fh_drop_write(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
  	if (exp) {
a09581f29   Stanislav Kinsbursky   nfsd: use exp_put...
634
  		exp_put(exp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
  		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;
  }
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
658
659
660
661
662
663
664
665
666
  
  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:
8e498751f   J. Bruce Fields   nfsd: move some o...
667
  		if (exp_sb(fhp->fh_export)->s_type->fs_flags & FS_REQUIRES_DEV)
b8da0d1c2   Neil Brown   knfsd: Validate f...
668
669
  			return FSIDSOURCE_DEV;
  		break;
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
670
  	case FSID_NUM:
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
671
672
  		if (fhp->fh_export->ex_flags & NFSEXP_FSID)
  			return FSIDSOURCE_FSID;
b8da0d1c2   Neil Brown   knfsd: Validate f...
673
674
675
  		break;
  	default:
  		break;
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
676
  	}
b8da0d1c2   Neil Brown   knfsd: Validate f...
677
678
679
680
681
682
683
684
  	/* 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;
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
685
  }