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);
f419a2e3b   Al Viro   [PATCH] kill name...
39
  		err = inode_permission(parent->d_inode, 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)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  		dprintk("nfsd_acceptable failed at %p %s
  ", tdentry, tdentry->d_name.name);
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
175a4eb7e   Al Viro   fs: propagate umo...
61
  nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, umode_t requested)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
  {
e10f9e141   J. Bruce Fields   nfsd: clean up nf...
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  	mode &= S_IFMT;
  
  	if (requested == 0) /* the caller doesn't care */
  		return nfs_ok;
  	if (mode == requested)
  		return nfs_ok;
  	/*
  	 * 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
80
  }
6fa02839b   J. Bruce Fields   nfsd4: recheck fo...
81
82
83
  static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
  					  struct svc_export *exp)
  {
12045a6ee   J. Bruce Fields   nfsd: let "insecu...
84
  	int flags = nfsexp_flags(rqstp, exp);
6fa02839b   J. Bruce Fields   nfsd4: recheck fo...
85
  	/* Check if the request originated from a secure port. */
3d354cbc4   J. Bruce Fields   nfsd: fix "insecu...
86
  	if (!rqstp->rq_secure && !(flags & NFSEXP_INSECURE_PORT)) {
5216a8e70   Pavel Emelyanov   Wrap buffers used...
87
  		RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
6fa02839b   J. Bruce Fields   nfsd4: recheck fo...
88
89
90
91
92
93
94
95
96
97
  		dprintk(KERN_WARNING
  		       "nfsd: request from insecure port %s!
  ",
  		       svc_print_addr(rqstp, buf, sizeof(buf)));
  		return nfserr_perm;
  	}
  
  	/* Set user creds for this exportpoint */
  	return nfserrno(nfsd_setuser(rqstp, exp));
  }
03a816b46   Steve Dickson   nfsd: restrict fi...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  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:
  	 */
  	if (unlikely(!S_ISDIR(dentry->d_inode->i_mode) &&
  		     !S_ISLNK(dentry->d_inode->i_mode)))
  		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
127
  /*
03550fac0   J. Bruce Fields   nfsd: move most o...
128
129
130
131
132
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
   * 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;
  			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];
  		}
  		data_left -= len;
  		if (data_left < 0)
  			return error;
  		exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth);
  		fid = (struct fid *)(fh->fh_auth + len);
  	} 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...
190
191
192
193
194
195
196
197
198
199
  	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 ...
200
201
202
203
204
205
206
207
  		struct cred *new = prepare_creds();
  		if (!new)
  			return nfserrno(-ENOMEM);
  		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...
208
209
210
211
212
  	} else {
  		error = nfsd_setuser_and_check_port(rqstp, exp);
  		if (error)
  			goto out;
  	}
03550fac0   J. Bruce Fields   nfsd: move most o...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
  
  	/*
  	 * 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;
  	}
  
  	if (S_ISDIR(dentry->d_inode->i_mode) &&
  			(dentry->d_flags & DCACHE_DISCONNECTED)) {
  		printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s
  ",
  				dentry->d_parent->d_name.name, dentry->d_name.name);
  	}
  
  	fhp->fh_dentry = dentry;
  	fhp->fh_export = exp;
03550fac0   J. Bruce Fields   nfsd: move most o...
258
259
260
261
262
  	return 0;
  out:
  	exp_put(exp);
  	return error;
  }
b3d47676d   J. Bruce Fields   nfsd: update fh_v...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  /**
   * 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
281
   *
b3d47676d   J. Bruce Fields   nfsd: update fh_v...
282
283
284
285
   * @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
286
   *
b3d47676d   J. Bruce Fields   nfsd: update fh_v...
287
288
   * @access is formed from the NFSD_MAY_* constants defined in
   * include/linux/nfsd/nfsd.h.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
   */
83b11340d   Al Viro   [PATCH] nfsfh sim...
290
  __be32
175a4eb7e   Al Viro   fs: propagate umo...
291
  fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  {
03550fac0   J. Bruce Fields   nfsd: move most o...
293
  	struct svc_export *exp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  	struct dentry	*dentry;
03550fac0   J. Bruce Fields   nfsd: move most o...
295
  	__be32		error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
  
  	dprintk("nfsd: fh_verify(%s)
  ", SVCFH_fmt(fhp));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
  	if (!fhp->fh_dentry) {
03550fac0   J. Bruce Fields   nfsd: move most o...
300
  		error = nfsd_set_fh_dentry(rqstp, fhp);
d1bbf14f3   NeilBrown   [PATCH] knfsd: Fi...
301
302
  		if (error)
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
  	}
864f0f61f   J. Bruce Fields   nfsd: simplify fh...
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  	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...
322
323
324
  	error = check_pseudo_root(rqstp, dentry, exp);
  	if (error)
  		goto out;
864f0f61f   J. Bruce Fields   nfsd: simplify fh...
325
326
327
  	error = nfsd_setuser_and_check_port(rqstp, exp);
  	if (error)
  		goto out;
7fc90ec93   J. Bruce Fields   [PATCH] knfsd: nf...
328

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
  	error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
  	if (error)
  		goto out;
04716e662   J. Bruce Fields   nfsd: permit unau...
332
333
334
335
336
  	/*
  	 * 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...
337
  	if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
04716e662   J. Bruce Fields   nfsd: permit unau...
338
339
340
341
342
343
344
345
346
347
348
349
350
  		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...
351

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  	if (error) {
34e9a63b4   NeilBrown   [PATCH] knfsd: ra...
357
358
359
360
361
  		dprintk("fh_verify: %s/%s permission failure, "
  			"acc=%x, error=%d
  ",
  			dentry->d_parent->d_name.name,
  			dentry->d_name.name,
fc2dd2e51   Al Viro   [PATCH] endiannes...
362
  			access, ntohl(error));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
371
372
373
374
375
376
377
  	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...
378
379
  static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
  		struct dentry *dentry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  {
547754916   Jan Blunck   Use struct path i...
381
  	if (dentry != exp->ex_path.dentry) {
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
382
383
384
385
  		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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386

6e91ea2bb   Christoph Hellwig   exportfs: add fid...
387
388
389
390
391
392
  		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
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  }
  
  /*
   * 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;
  }
8e498751f   J. Bruce Fields   nfsd: move some o...
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
  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)
  {
  	return exp->ex_path.dentry->d_inode->i_sb;
  }
  
  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
441

bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
442
443
  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...
444
  	u8 version;
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
445
446
  	u8 fsid_type;
  retry:
b41eeef14   NeilBrown   knfsd: avoid Oops...
447
  	version = 1;
7e4053645   NeilBrown   [PATCH] knfsd: ig...
448
  	if (ref_fh && ref_fh->fh_export == exp) {
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
449
  		version = ref_fh->fh_handle.fh_version;
b41eeef14   NeilBrown   knfsd: avoid Oops...
450
  		fsid_type = ref_fh->fh_handle.fh_fsid_type;
b41eeef14   NeilBrown   knfsd: avoid Oops...
451
452
453
454
  		ref_fh = NULL;
  
  		switch (version) {
  		case 0xca:
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
455
  			fsid_type = FSID_DEV;
b41eeef14   NeilBrown   knfsd: avoid Oops...
456
457
458
459
460
461
  			break;
  		case 1:
  			break;
  		default:
  			goto retry;
  		}
8e498751f   J. Bruce Fields   nfsd: move some o...
462
463
464
465
466
  		/*
  		 * 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...
467
  		 */
8e498751f   J. Bruce Fields   nfsd: move some o...
468
469
  		if (!fsid_type_ok_for_exp(fsid_type, exp))
  			goto retry;
30fa8c015   Steve Dickson   NFSD: FIDs need t...
470
471
  	} else if (exp->ex_flags & NFSEXP_FSID) {
  		fsid_type = FSID_NUM;
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
472
473
  	} else if (exp->ex_uuid) {
  		if (fhp->fh_maxsize >= 64) {
8e498751f   J. Bruce Fields   nfsd: move some o...
474
  			if (is_root_export(exp))
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
475
476
477
478
  				fsid_type = FSID_UUID16;
  			else
  				fsid_type = FSID_UUID16_INUM;
  		} else {
8e498751f   J. Bruce Fields   nfsd: move some o...
479
  			if (is_root_export(exp))
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
480
481
482
483
  				fsid_type = FSID_UUID8;
  			else
  				fsid_type = FSID_UUID4_INUM;
  		}
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
484
  	} else if (!old_valid_dev(exp_sb(exp)->s_dev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  		/* for newer device numbers, we must use a newer fsid format */
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
486
  		fsid_type = FSID_ENCODE_DEV;
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
487
  	else
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
488
  		fsid_type = FSID_DEV;
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
  	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
  	 *
  	 */
  
  	struct inode * inode = dentry->d_inode;
  	struct dentry *parent = dentry->d_parent;
  	__u32 *datap;
  	dev_t ex_dev = exp_sb(exp)->s_dev;
  
  	dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)
  ",
  		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.
  	 */
  	 set_version_and_fsid_type(fhp, exp, ref_fh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
528
529
  
  	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!
  ",
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
530
  		       parent->d_name.name, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
534
  	}
  	if (fhp->fh_maxsize < NFS_FHSIZE)
  		printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s
  ",
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
535
536
  		       fhp->fh_maxsize,
  		       parent->d_name.name, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
  
  	fhp->fh_dentry = dget(dentry); /* our internal copy */
  	fhp->fh_export = exp;
  	cache_get(&exp->h);
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
541
  	if (fhp->fh_handle.fh_version == 0xca) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
545
546
547
  		/* 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...
548
  		fhp->fh_handle.ofh_xino =
547754916   Jan Blunck   Use struct path i...
549
  			ino_t_to_u32(exp->ex_path.dentry->d_inode->i_ino);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
552
553
554
  		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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
  		fhp->fh_handle.fh_auth_type = 0;
  		datap = fhp->fh_handle.fh_auth+0;
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
557
  		mk_fsid(fhp->fh_handle.fh_fsid_type, datap, ex_dev,
547754916   Jan Blunck   Use struct path i...
558
  			exp->ex_path.dentry->d_inode->i_ino,
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
559
  			exp->ex_fsid, exp->ex_uuid);
bc6c53d5a   J. Bruce Fields   nfsd: move fsid_t...
560
  		len = key_len(fhp->fh_handle.fh_fsid_type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
  		datap += len/4;
  		fhp->fh_handle.fh_size = 4 + len;
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
563
564
  		if (inode)
  			_fh_update(fhp, exp, dentry);
1be10a88c   J. Bruce Fields   nfsd4: filehandle...
565
566
  		if (fhp->fh_handle.fh_fileid_type == 255) {
  			fh_put(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
  			return nfserr_opnotsupp;
1be10a88c   J. Bruce Fields   nfsd4: filehandle...
568
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
573
574
575
576
  	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...
577
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
580
  fh_update(struct svc_fh *fhp)
  {
  	struct dentry *dentry;
982aedfd0   NeilBrown   [PATCH] knfsd: ti...
581

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
583
584
585
586
587
588
589
590
  	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 {
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
591
  		if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT)
4c9608b2f   NeilBrown   [PATCH] knfsd: re...
592
  			goto out;
6e91ea2bb   Christoph Hellwig   exportfs: add fid...
593
594
  
  		_fh_update(fhp, fhp->fh_export, dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
  		if (fhp->fh_handle.fh_fileid_type == 255)
  			return nfserr_opnotsupp;
  	}
  out:
  	return 0;
  
  out_bad:
  	printk(KERN_ERR "fh_update: fh not verified!
  ");
  	goto out;
  out_negative:
  	printk(KERN_ERR "fh_update: %s/%s still negative!
  ",
  		dentry->d_parent->d_name.name, dentry->d_name.name);
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  }
  
  /*
   * 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
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
  	}
  	if (exp) {
baab935ff   NeilBrown   [PATCH] knfsd: Co...
630
  		cache_put(&exp->h, &svc_export_cache);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  		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...
654
655
656
657
658
659
660
661
662
  
  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...
663
  		if (exp_sb(fhp->fh_export)->s_type->fs_flags & FS_REQUIRES_DEV)
b8da0d1c2   Neil Brown   knfsd: Validate f...
664
665
  			return FSIDSOURCE_DEV;
  		break;
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
666
  	case FSID_NUM:
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
667
668
  		if (fhp->fh_export->ex_flags & NFSEXP_FSID)
  			return FSIDSOURCE_FSID;
b8da0d1c2   Neil Brown   knfsd: Validate f...
669
670
671
  		break;
  	default:
  		break;
af6a4e280   NeilBrown   [PATCH] knfsd: ad...
672
  	}
b8da0d1c2   Neil Brown   knfsd: Validate f...
673
674
675
676
677
678
679
680
  	/* 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...
681
  }