Blame view

fs/nfsd/vfs.c 54 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
9
10
11
12
13
14
   * File operations used by nfsd. Some of these have been ripped from
   * other parts of the kernel because they weren't exported, others
   * are partial duplicates with added or changed functionality.
   *
   * Note that several functions dget() the dentry upon which they want
   * to act, most notably those that create directory entries. Response
   * dentry's are dput()'d if necessary in the release callback.
   * So if you notice code paths that apparently fail to dput() the
   * dentry, don't worry--they have been taken care of.
   *
   * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
   * Zerocpy NFS support (C) 2002 Hirokazu Takahashi <taka@valinux.co.jp>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  #include <linux/fs.h>
  #include <linux/file.h>
d6b29d7ce   Jens Axboe   splice: divorce t...
17
  #include <linux/splice.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/fcntl.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/namei.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  #include <linux/delay.h>
0eeca2830   Robert Love   [PATCH] inotify
21
  #include <linux/fsnotify.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/posix_acl_xattr.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  #include <linux/xattr.h>
9a74af213   Boaz Harrosh   nfsd: Move privat...
24
25
  #include <linux/jhash.h>
  #include <linux/ima.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
26
  #include <linux/slab.h>
9a74af213   Boaz Harrosh   nfsd: Move privat...
27
  #include <asm/uaccess.h>
f501912a3   Ben Myers   commit_metadata e...
28
29
  #include <linux/exportfs.h>
  #include <linux/writeback.h>
9a74af213   Boaz Harrosh   nfsd: Move privat...
30
31
32
33
  
  #ifdef CONFIG_NFSD_V3
  #include "xdr3.h"
  #endif /* CONFIG_NFSD_V3 */
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
34
  #ifdef CONFIG_NFSD_V4
2ca72e17e   J. Bruce Fields   nfsd4: move idmap...
35
36
  #include "acl.h"
  #include "idmap.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  #endif /* CONFIG_NFSD_V4 */
9a74af213   Boaz Harrosh   nfsd: Move privat...
38
39
  #include "nfsd.h"
  #include "vfs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
  
  #define NFSDDBG_FACILITY		NFSDDBG_FILEOP
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  /*
   * This is a cache of readahead params that help us choose the proper
   * readahead strategy. Initially, we set all readahead parameters to 0
   * and let the VFS handle things.
   * If you increase the number of cached files very much, you'll need to
   * add a hash table here.
   */
  struct raparms {
  	struct raparms		*p_next;
  	unsigned int		p_count;
  	ino_t			p_ino;
  	dev_t			p_dev;
  	int			p_set;
  	struct file_ra_state	p_ra;
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
57
  	unsigned int		p_hindex;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
  };
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
59
60
61
62
  struct raparm_hbucket {
  	struct raparms		*pb_head;
  	spinlock_t		pb_lock;
  } ____cacheline_aligned_in_smp;
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
63
64
65
66
  #define RAPARM_HASH_BITS	4
  #define RAPARM_HASH_SIZE	(1<<RAPARM_HASH_BITS)
  #define RAPARM_HASH_MASK	(RAPARM_HASH_SIZE-1)
  static struct raparm_hbucket	raparm_hash[RAPARM_HASH_SIZE];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
  
  /* 
   * Called from nfsd_lookup and encode_dirent. Check if we have crossed 
   * a mount point.
e0bb89ef0   J.Bruce Fields   [PATCH] knfsd: nf...
71
   * Returns -EAGAIN or -ETIMEDOUT leaving *dpp and *expp unchanged,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
77
78
79
   *  or nfs_ok having possibly changed *dpp and *expp
   */
  int
  nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, 
  		        struct svc_export **expp)
  {
  	struct svc_export *exp = *expp, *exp2 = NULL;
  	struct dentry *dentry = *dpp;
91c9fa8f7   Al Viro   switch rqst_exp_g...
80
81
  	struct path path = {.mnt = mntget(exp->ex_path.mnt),
  			    .dentry = dget(dentry)};
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
82
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83

7cc90cc3f   Al Viro   don't pass 'mount...
84
  	err = follow_down(&path);
cc53ce53c   David Howells   Add a dentry op t...
85
86
  	if (err < 0)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87

91c9fa8f7   Al Viro   switch rqst_exp_g...
88
  	exp2 = rqst_exp_get_by_name(rqstp, &path);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  	if (IS_ERR(exp2)) {
3b6cee7bc   J. Bruce Fields   nfsd4: don't cont...
90
91
92
93
94
95
96
97
98
99
  		err = PTR_ERR(exp2);
  		/*
  		 * We normally allow NFS clients to continue
  		 * "underneath" a mountpoint that is not exported.
  		 * The exception is V4ROOT, where no traversal is ever
  		 * allowed without an explicit export of the new
  		 * directory.
  		 */
  		if (err == -ENOENT && !(exp->ex_flags & NFSEXP_V4ROOT))
  			err = 0;
91c9fa8f7   Al Viro   switch rqst_exp_g...
100
  		path_put(&path);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
  		goto out;
  	}
3c394ddaa   Steve Dickson   nfsd4: nfsv4 clie...
103
104
  	if (nfsd_v4client(rqstp) ||
  		(exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
  		/* successfully crossed mount point */
1644ccc8a   Al Viro   Safer nfsd_cross_...
106
  		/*
91c9fa8f7   Al Viro   switch rqst_exp_g...
107
108
109
110
  		 * This is subtle: path.dentry is *not* on path.mnt
  		 * at this point.  The only reason we are safe is that
  		 * original mnt is pinned down by exp, so we should
  		 * put path *before* putting exp
1644ccc8a   Al Viro   Safer nfsd_cross_...
111
  		 */
91c9fa8f7   Al Viro   switch rqst_exp_g...
112
113
  		*dpp = path.dentry;
  		path.dentry = dentry;
1644ccc8a   Al Viro   Safer nfsd_cross_...
114
  		*expp = exp2;
91c9fa8f7   Al Viro   switch rqst_exp_g...
115
  		exp2 = exp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  	}
91c9fa8f7   Al Viro   switch rqst_exp_g...
117
118
  	path_put(&path);
  	exp_put(exp2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
  out:
  	return err;
  }
289ede453   J. Bruce Fields   nfsd: minor nfsd_...
122
123
124
125
126
127
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
  static void follow_to_parent(struct path *path)
  {
  	struct dentry *dp;
  
  	while (path->dentry == path->mnt->mnt_root && follow_up(path))
  		;
  	dp = dget_parent(path->dentry);
  	dput(path->dentry);
  	path->dentry = dp;
  }
  
  static int nfsd_lookup_parent(struct svc_rqst *rqstp, struct dentry *dparent, struct svc_export **exp, struct dentry **dentryp)
  {
  	struct svc_export *exp2;
  	struct path path = {.mnt = mntget((*exp)->ex_path.mnt),
  			    .dentry = dget(dparent)};
  
  	follow_to_parent(&path);
  
  	exp2 = rqst_exp_parent(rqstp, &path);
  	if (PTR_ERR(exp2) == -ENOENT) {
  		*dentryp = dget(dparent);
  	} else if (IS_ERR(exp2)) {
  		path_put(&path);
  		return PTR_ERR(exp2);
  	} else {
  		*dentryp = dget(path.dentry);
  		exp_put(*exp);
  		*exp = exp2;
  	}
  	path_put(&path);
  	return 0;
  }
82ead7fe4   J. Bruce Fields   nfsd: filter look...
155
156
157
158
  /*
   * For nfsd purposes, we treat V4ROOT exports as though there was an
   * export at *every* directory.
   */
3227fa41a   J. Bruce Fields   nfsd: filter read...
159
  int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
82ead7fe4   J. Bruce Fields   nfsd: filter look...
160
161
162
  {
  	if (d_mountpoint(dentry))
  		return 1;
11fcee029   Trond Myklebust   NFSD: Add a cache...
163
164
  	if (nfsd4_is_junction(dentry))
  		return 1;
82ead7fe4   J. Bruce Fields   nfsd: filter look...
165
166
167
168
  	if (!(exp->ex_flags & NFSEXP_V4ROOT))
  		return 0;
  	return dentry->d_inode != NULL;
  }
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
169
  __be32
6c0a654dc   J. Bruce Fields   knfsd: nfsd: fact...
170
  nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
5a022fc87   Chuck Lever   NFSD: Adjust file...
171
  		   const char *name, unsigned int len,
6c0a654dc   J. Bruce Fields   knfsd: nfsd: fact...
172
  		   struct svc_export **exp_ret, struct dentry **dentry_ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
176
  {
  	struct svc_export	*exp;
  	struct dentry		*dparent;
  	struct dentry		*dentry;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
177
  	int			host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
  
  	dprintk("nfsd: nfsd_lookup(fh %s, %.*s)
  ", SVCFH_fmt(fhp), len,name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
  	dparent = fhp->fh_dentry;
  	exp  = fhp->fh_export;
  	exp_get(exp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
  	/* Lookup the name, but don't follow links */
  	if (isdotent(name, len)) {
  		if (len==1)
  			dentry = dget(dparent);
547754916   Jan Blunck   Use struct path i...
188
  		else if (dparent != exp->ex_path.dentry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  			dentry = dget_parent(dparent);
fed838112   J. Bruce Fields   nfsd4: cross moun...
190
  		else if (!EX_NOHIDE(exp) && !nfsd_v4client(rqstp))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
  			dentry = dget(dparent); /* .. == . just like at / */
  		else {
  			/* checking mountpoint crossing is very different when stepping up */
289ede453   J. Bruce Fields   nfsd: minor nfsd_...
194
195
  			host_err = nfsd_lookup_parent(rqstp, dparent, &exp, &dentry);
  			if (host_err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  				goto out_nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
  		}
  	} else {
  		fh_lock(fhp);
  		dentry = lookup_one_len(name, dparent, len);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
201
  		host_err = PTR_ERR(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
  		if (IS_ERR(dentry))
  			goto out_nfserr;
  		/*
  		 * check if we have crossed a mount point ...
  		 */
82ead7fe4   J. Bruce Fields   nfsd: filter look...
207
  		if (nfsd_mountpoint(dentry, exp)) {
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
208
  			if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
213
  				dput(dentry);
  				goto out_nfserr;
  			}
  		}
  	}
6c0a654dc   J. Bruce Fields   knfsd: nfsd: fact...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  	*dentry_ret = dentry;
  	*exp_ret = exp;
  	return 0;
  
  out_nfserr:
  	exp_put(exp);
  	return nfserrno(host_err);
  }
  
  /*
   * Look up one component of a pathname.
   * N.B. After this call _both_ fhp and resfh need an fh_put
   *
   * If the lookup would cross a mountpoint, and the mounted filesystem
   * is exported to the client with NFSEXP_NOHIDE, then the lookup is
   * accepted as it stands and the mounted directory is
   * returned. Otherwise the covered directory is returned.
   * NOTE: this mountpoint crossing is not supported properly by all
   *   clients and is explicitly disallowed for NFSv3
   *      NeilBrown <neilb@cse.unsw.edu.au>
   */
  __be32
  nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
5a022fc87   Chuck Lever   NFSD: Adjust file...
237
  				unsigned int len, struct svc_fh *resfh)
6c0a654dc   J. Bruce Fields   knfsd: nfsd: fact...
238
239
240
241
  {
  	struct svc_export	*exp;
  	struct dentry		*dentry;
  	__be32 err;
29a78a3ed   J. Bruce Fields   nfsd4: make fh_ve...
242
243
244
  	err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
  	if (err)
  		return err;
6c0a654dc   J. Bruce Fields   knfsd: nfsd: fact...
245
246
247
  	err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry);
  	if (err)
  		return err;
32c1eb0cd   Andy Adamson   knfsd: nfsd4: ret...
248
249
250
  	err = check_nfsd_access(exp, rqstp);
  	if (err)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
256
257
  	/*
  	 * Note: we compose the file handle now, but as the
  	 * dentry may be negative, it may need to be updated.
  	 */
  	err = fh_compose(resfh, exp, dentry, fhp);
  	if (!err && !dentry->d_inode)
  		err = nfserr_noent;
32c1eb0cd   Andy Adamson   knfsd: nfsd4: ret...
258
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  	dput(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
  	exp_put(exp);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
  }
4795bb37e   J. Bruce Fields   nfsd: break lease...
263
264
265
266
267
268
  static int nfsd_break_lease(struct inode *inode)
  {
  	if (!S_ISREG(inode->i_mode))
  		return 0;
  	return break_lease(inode, O_WRONLY | O_NONBLOCK);
  }
f501912a3   Ben Myers   commit_metadata e...
269
270
271
272
273
274
275
276
  /*
   * Commit metadata changes to stable storage.
   */
  static int
  commit_metadata(struct svc_fh *fhp)
  {
  	struct inode *inode = fhp->fh_dentry->d_inode;
  	const struct export_operations *export_ops = inode->i_sb->s_export_op;
f501912a3   Ben Myers   commit_metadata e...
277
278
279
  
  	if (!EX_ISSYNC(fhp->fh_export))
  		return 0;
c37650161   Christoph Hellwig   fs: add sync_inod...
280
281
282
  	if (export_ops->commit_metadata)
  		return export_ops->commit_metadata(inode);
  	return sync_inode_metadata(inode, 1);
f501912a3   Ben Myers   commit_metadata e...
283
  }
6c0a654dc   J. Bruce Fields   knfsd: nfsd: fact...
284

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
  /*
   * Set various file attributes.
   * N.B. After this call fhp needs an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
289
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
  nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
  	     int check_guard, time_t guardtime)
  {
  	struct dentry	*dentry;
  	struct inode	*inode;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
295
  	int		accmode = NFSD_MAY_SATTR;
175a4eb7e   Al Viro   fs: propagate umo...
296
  	umode_t		ftype = 0;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
297
298
  	__be32		err;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
  	int		size_change = 0;
  
  	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
302
  		accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
  	if (iap->ia_valid & ATTR_SIZE)
  		ftype = S_IFREG;
  
  	/* Get inode */
  	err = fh_verify(rqstp, fhp, ftype, accmode);
15b7a1b86   NeilBrown   [PATCH] knfsd: fi...
308
  	if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
  		goto out;
  
  	dentry = fhp->fh_dentry;
  	inode = dentry->d_inode;
15b7a1b86   NeilBrown   [PATCH] knfsd: fi...
313
314
315
316
317
318
  	/* Ignore any mode updates on symlinks */
  	if (S_ISLNK(inode->i_mode))
  		iap->ia_valid &= ~ATTR_MODE;
  
  	if (!iap->ia_valid)
  		goto out;
9c85fca56   Christoph Hellwig   nfsd: fix horribl...
319
320
  	/*
  	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
325
326
327
328
329
330
331
332
  	 * which only requires access, and "set-[ac]time-to-X" which
  	 * requires ownership.
  	 * So if it looks like it might be "set both to the same time which
  	 * is close to now", and if inode_change_ok fails, then we
  	 * convert to "set to now" instead of "set to explicit time"
  	 *
  	 * We only call inode_change_ok as the last test as technically
  	 * it is not an interface that we should be using.  It is only
  	 * valid if the filesystem does not define it's own i_op->setattr.
  	 */
  #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
  #define	MAX_TOUCH_TIME_ERROR (30*60)
9c85fca56   Christoph Hellwig   nfsd: fix horribl...
333
334
335
336
337
338
339
340
  	if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
  	    iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) {
  		/*
  		 * Looks probable.
  		 *
  		 * Now just make sure time is in the right ballpark.
  		 * Solaris, at least, doesn't seem to care what the time
  		 * request is.  We require it be within 30 minutes of now.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
  		 */
9c85fca56   Christoph Hellwig   nfsd: fix horribl...
342
343
344
345
346
347
348
349
350
351
352
353
  		time_t delta = iap->ia_atime.tv_sec - get_seconds();
  		if (delta < 0)
  			delta = -delta;
  		if (delta < MAX_TOUCH_TIME_ERROR &&
  		    inode_change_ok(inode, iap) != 0) {
  			/*
  			 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
  			 * This will cause notify_change to set these times
  			 * to "now"
  			 */
  			iap->ia_valid &= ~BOTH_TIME_SET;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
  	}
  	    
9c85fca56   Christoph Hellwig   nfsd: fix horribl...
356
357
358
359
  	/*
  	 * The size case is special.
  	 * It changes the file as well as the attributes.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
  	if (iap->ia_valid & ATTR_SIZE) {
  		if (iap->ia_size < inode->i_size) {
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
362
363
  			err = nfsd_permission(rqstp, fhp->fh_export, dentry,
  					NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
366
  			if (err)
  				goto out;
  		}
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
367
368
  		host_err = get_write_access(inode);
  		if (host_err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
  			goto out_nfserr;
  
  		size_change = 1;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
372
373
  		host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
  		if (host_err) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
  			put_write_access(inode);
  			goto out_nfserr;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  	}
ca456252d   Jeff Layton   knfsd: clear both...
378
  	/* sanitize the mode change */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
  	if (iap->ia_valid & ATTR_MODE) {
  		iap->ia_mode &= S_IALLUGO;
dee3209d9   Jeff Layton   knfsd: get rid of...
381
  		iap->ia_mode |= (inode->i_mode & ~S_IALLUGO);
ca456252d   Jeff Layton   knfsd: clear both...
382
383
384
  	}
  
  	/* Revoke setuid/setgid on chown */
0953e620d   Sachin S. Prabhu   Inconsistent seta...
385
386
387
  	if (!S_ISDIR(inode->i_mode) &&
  	    (((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) ||
  	     ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid))) {
ca456252d   Jeff Layton   knfsd: clear both...
388
389
390
  		iap->ia_valid |= ATTR_KILL_PRIV;
  		if (iap->ia_valid & ATTR_MODE) {
  			/* we're setting mode too, just clear the s*id bits */
8a0ce7d99   Jeff Layton   knfsd: only set A...
391
  			iap->ia_mode &= ~S_ISUID;
ca456252d   Jeff Layton   knfsd: clear both...
392
393
394
395
396
  			if (iap->ia_mode & S_IXGRP)
  				iap->ia_mode &= ~S_ISGID;
  		} else {
  			/* set ATTR_KILL_* bits and let VFS handle it */
  			iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
8a0ce7d99   Jeff Layton   knfsd: only set A...
397
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
400
401
402
403
404
  	/* Change the attributes. */
  
  	iap->ia_valid |= ATTR_CTIME;
  
  	err = nfserr_notsync;
  	if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
4795bb37e   J. Bruce Fields   nfsd: break lease...
405
  		host_err = nfsd_break_lease(inode);
6a76bebef   J. Bruce Fields   nfsd4: break leas...
406
407
  		if (host_err)
  			goto out_nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  		fh_lock(fhp);
6a76bebef   J. Bruce Fields   nfsd4: break leas...
409

6264d69d7   Al Viro   [PATCH] nfsd: vfs...
410
411
  		host_err = notify_change(dentry, iap);
  		err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
416
  		fh_unlock(fhp);
  	}
  	if (size_change)
  		put_write_access(inode);
  	if (!err)
b160fdabe   Christoph Hellwig   nfsd: nfsd_setatt...
417
  		commit_metadata(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
421
  out:
  	return err;
  
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
422
  	err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
  	goto out;
  }
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
425
426
427
428
429
430
  #if defined(CONFIG_NFSD_V2_ACL) || \
      defined(CONFIG_NFSD_V3_ACL) || \
      defined(CONFIG_NFSD_V4)
  static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf)
  {
  	ssize_t buflen;
6c6a426fd   Krishna Kumar   nfsd: Fix memory ...
431
  	ssize_t ret;
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
432
433
434
435
  
  	buflen = vfs_getxattr(dentry, key, NULL, 0);
  	if (buflen <= 0)
  		return buflen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436

5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
437
438
439
  	*buf = kmalloc(buflen, GFP_KERNEL);
  	if (!*buf)
  		return -ENOMEM;
6c6a426fd   Krishna Kumar   nfsd: Fix memory ...
440
441
442
443
  	ret = vfs_getxattr(dentry, key, *buf, buflen);
  	if (ret < 0)
  		kfree(*buf);
  	return ret;
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
444
445
446
447
  }
  #endif
  
  #if defined(CONFIG_NFSD_V4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
  static int
  set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
  {
  	int len;
  	size_t buflen;
  	char *buf = NULL;
  	int error = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
458
459
460
461
462
463
464
465
466
  
  	buflen = posix_acl_xattr_size(pacl->a_count);
  	buf = kmalloc(buflen, GFP_KERNEL);
  	error = -ENOMEM;
  	if (buf == NULL)
  		goto out;
  
  	len = posix_acl_to_xattr(pacl, buf, buflen);
  	if (len < 0) {
  		error = len;
  		goto out;
  	}
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
467
  	error = vfs_setxattr(dentry, key, buf, len, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
470
471
  out:
  	kfree(buf);
  	return error;
  }
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
472
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
  nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
      struct nfs4_acl *acl)
  {
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
476
477
  	__be32 error;
  	int host_error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
480
481
482
483
  	struct dentry *dentry;
  	struct inode *inode;
  	struct posix_acl *pacl = NULL, *dpacl = NULL;
  	unsigned int flags = 0;
  
  	/* Get inode */
e281d8100   J. Bruce Fields   nfsd4: fix incorr...
484
  	error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  	if (error)
4b2ca38ad   J. Bruce Fields   knfsd: nfsd4: fix...
486
  		return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
490
491
  
  	dentry = fhp->fh_dentry;
  	inode = dentry->d_inode;
  	if (S_ISDIR(inode->i_mode))
  		flags = NFS4_ACL_DIR;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
492
493
  	host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
  	if (host_error == -EINVAL) {
4b2ca38ad   J. Bruce Fields   knfsd: nfsd4: fix...
494
  		return nfserr_attrnotsupp;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
495
  	} else if (host_error < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
  		goto out_nfserr;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
497
498
  	host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS);
  	if (host_error < 0)
4b2ca38ad   J. Bruce Fields   knfsd: nfsd4: fix...
499
  		goto out_release;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500

4b2ca38ad   J. Bruce Fields   knfsd: nfsd4: fix...
501
  	if (S_ISDIR(inode->i_mode))
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
502
  		host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503

4b2ca38ad   J. Bruce Fields   knfsd: nfsd4: fix...
504
  out_release:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
  	posix_acl_release(pacl);
  	posix_acl_release(dpacl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  out_nfserr:
f34f92427   J. Bruce Fields   [PATCH] knfsd: nf...
508
  	if (host_error == -EOPNOTSUPP)
4b2ca38ad   J. Bruce Fields   knfsd: nfsd4: fix...
509
  		return nfserr_attrnotsupp;
f34f92427   J. Bruce Fields   [PATCH] knfsd: nf...
510
  	else
4b2ca38ad   J. Bruce Fields   knfsd: nfsd4: fix...
511
  		return nfserrno(host_error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
514
515
516
  }
  
  static struct posix_acl *
  _get_posix_acl(struct dentry *dentry, char *key)
  {
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
517
  	void *buf = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  	struct posix_acl *pacl = NULL;
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
519
  	int buflen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520

5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
521
522
523
524
525
  	buflen = nfsd_getxattr(dentry, key, &buf);
  	if (!buflen)
  		buflen = -ENODATA;
  	if (buflen <= 0)
  		return ERR_PTR(buflen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
  
  	pacl = posix_acl_from_xattr(buf, buflen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
529
  	kfree(buf);
  	return pacl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
532
533
534
535
536
537
538
  }
  
  int
  nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
  {
  	struct inode *inode = dentry->d_inode;
  	int error = 0;
  	struct posix_acl *pacl = NULL, *dpacl = NULL;
  	unsigned int flags = 0;
9a59f452a   Christoph Hellwig   [PATCH] remove <l...
539
  	pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
544
545
546
547
548
  	if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
  		pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
  	if (IS_ERR(pacl)) {
  		error = PTR_ERR(pacl);
  		pacl = NULL;
  		goto out;
  	}
  
  	if (S_ISDIR(inode->i_mode)) {
9a59f452a   Christoph Hellwig   [PATCH] remove <l...
549
  		dpacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_DEFAULT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
  		if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
  			dpacl = NULL;
  		else if (IS_ERR(dpacl)) {
  			error = PTR_ERR(dpacl);
  			dpacl = NULL;
  			goto out;
  		}
  		flags = NFS4_ACL_DIR;
  	}
  
  	*acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
  	if (IS_ERR(*acl)) {
  		error = PTR_ERR(*acl);
  		*acl = NULL;
  	}
   out:
  	posix_acl_release(pacl);
  	posix_acl_release(dpacl);
  	return error;
  }
9b4146e85   Chuck Lever   NFSD: Change name...
570
571
572
573
574
575
576
577
578
579
580
581
582
  /*
   * NFS junction information is stored in an extended attribute.
   */
  #define NFSD_JUNCTION_XATTR_NAME	XATTR_TRUSTED_PREFIX "junction.nfs"
  
  /**
   * nfsd4_is_junction - Test if an object could be an NFS junction
   *
   * @dentry: object to test
   *
   * Returns 1 if "dentry" appears to contain NFS junction information.
   * Otherwise 0 is returned.
   */
11fcee029   Trond Myklebust   NFSD: Add a cache...
583
584
585
586
587
588
589
590
591
592
  int nfsd4_is_junction(struct dentry *dentry)
  {
  	struct inode *inode = dentry->d_inode;
  
  	if (inode == NULL)
  		return 0;
  	if (inode->i_mode & S_IXUGO)
  		return 0;
  	if (!(inode->i_mode & S_ISVTX))
  		return 0;
9b4146e85   Chuck Lever   NFSD: Change name...
593
  	if (vfs_getxattr(dentry, NFSD_JUNCTION_XATTR_NAME, NULL, 0) <= 0)
11fcee029   Trond Myklebust   NFSD: Add a cache...
594
595
596
  		return 0;
  	return 1;
  }
6a85d6c76   J. Bruce Fields   nfsd4: comment ni...
597
  #endif /* defined(CONFIG_NFSD_V4) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
603
604
605
606
607
  
  #ifdef CONFIG_NFSD_V3
  /*
   * Check server access rights to a file system object
   */
  struct accessmap {
  	u32		access;
  	int		how;
  };
  static struct accessmap	nfs3_regaccess[] = {
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
608
609
610
611
      {	NFS3_ACCESS_READ,	NFSD_MAY_READ			},
      {	NFS3_ACCESS_EXECUTE,	NFSD_MAY_EXEC			},
      {	NFS3_ACCESS_MODIFY,	NFSD_MAY_WRITE|NFSD_MAY_TRUNC	},
      {	NFS3_ACCESS_EXTEND,	NFSD_MAY_WRITE			},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
615
616
  
      {	0,			0				}
  };
  
  static struct accessmap	nfs3_diraccess[] = {
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
617
618
619
620
621
      {	NFS3_ACCESS_READ,	NFSD_MAY_READ			},
      {	NFS3_ACCESS_LOOKUP,	NFSD_MAY_EXEC			},
      {	NFS3_ACCESS_MODIFY,	NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC},
      {	NFS3_ACCESS_EXTEND,	NFSD_MAY_EXEC|NFSD_MAY_WRITE	},
      {	NFS3_ACCESS_DELETE,	NFSD_MAY_REMOVE			},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
625
626
627
628
629
630
631
632
633
  
      {	0,			0				}
  };
  
  static struct accessmap	nfs3_anyaccess[] = {
  	/* Some clients - Solaris 2.6 at least, make an access call
  	 * to the server to check for access for things like /dev/null
  	 * (which really, the server doesn't care about).  So
  	 * We provide simple access checking for them, looking
  	 * mainly at mode bits, and we make sure to ignore read-only
  	 * filesystem checks
  	 */
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
634
635
636
637
      {	NFS3_ACCESS_READ,	NFSD_MAY_READ			},
      {	NFS3_ACCESS_EXECUTE,	NFSD_MAY_EXEC			},
      {	NFS3_ACCESS_MODIFY,	NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS	},
      {	NFS3_ACCESS_EXTEND,	NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
  
      {	0,			0				}
  };
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
641
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
646
647
  nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *supported)
  {
  	struct accessmap	*map;
  	struct svc_export	*export;
  	struct dentry		*dentry;
  	u32			query, result = 0, sresult = 0;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
648
  	__be32			error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649

8837abcab   Miklos Szeredi   nfsd: rename MAY_...
650
  	error = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
  	if (error)
  		goto out;
  
  	export = fhp->fh_export;
  	dentry = fhp->fh_dentry;
  
  	if (S_ISREG(dentry->d_inode->i_mode))
  		map = nfs3_regaccess;
  	else if (S_ISDIR(dentry->d_inode->i_mode))
  		map = nfs3_diraccess;
  	else
  		map = nfs3_anyaccess;
  
  
  	query = *access;
  	for  (; map->access; map++) {
  		if (map->access & query) {
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
668
  			__be32 err2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
670
  
  			sresult |= map->access;
0ec757df9   J. Bruce Fields   knfsd: nfsd4: mak...
671
  			err2 = nfsd_permission(rqstp, export, dentry, map->how);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
  			switch (err2) {
  			case nfs_ok:
  				result |= map->access;
  				break;
  				
  			/* the following error codes just mean the access was not allowed,
  			 * rather than an error occurred */
  			case nfserr_rofs:
  			case nfserr_acces:
  			case nfserr_perm:
  				/* simply don't "or" in the access bit. */
  				break;
  			default:
  				error = err2;
  				goto out;
  			}
  		}
  	}
  	*access = result;
  	if (supported)
  		*supported = sresult;
  
   out:
  	return error;
  }
  #endif /* CONFIG_NFSD_V3 */
105f46221   J. Bruce Fields   nfsd4: fix break_...
698
699
700
  static int nfsd_open_break_lease(struct inode *inode, int access)
  {
  	unsigned int mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701

105f46221   J. Bruce Fields   nfsd4: fix break_...
702
703
704
705
706
  	if (access & NFSD_MAY_NOT_BREAK_LEASE)
  		return 0;
  	mode = (access & NFSD_MAY_WRITE) ? O_WRONLY : O_RDONLY;
  	return break_lease(inode, mode | O_NONBLOCK);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
711
712
  
  /*
   * Open an existing file or directory.
   * The access argument indicates the type of open (read/write/lock)
   * N.B. After this call fhp needs an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
713
  __be32
175a4eb7e   Al Viro   fs: propagate umo...
714
  nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
718
  			int access, struct file **filp)
  {
  	struct dentry	*dentry;
  	struct inode	*inode;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
719
720
  	int		flags = O_RDONLY|O_LARGEFILE;
  	__be32		err;
91885258e   Jeff Layton   nfsd: don't break...
721
  	int		host_err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722

e0e817392   David Howells   CRED: Add some co...
723
  	validate_process_creds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
725
726
727
728
  	/*
  	 * If we get here, then the client has already done an "open",
  	 * and (hopefully) checked permission - so allow OWNER_OVERRIDE
  	 * in case a chmod has now revoked permission.
  	 */
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
729
  	err = fh_verify(rqstp, fhp, type, access | NFSD_MAY_OWNER_OVERRIDE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
734
735
736
737
738
739
  	if (err)
  		goto out;
  
  	dentry = fhp->fh_dentry;
  	inode = dentry->d_inode;
  
  	/* Disallow write access to files with the append-only bit set
  	 * or any access when mandatory locking enabled
  	 */
  	err = nfserr_perm;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
740
  	if (IS_APPEND(inode) && (access & NFSD_MAY_WRITE))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
  		goto out;
5e7fc4364   J. Bruce Fields   nfsd: remove IS_I...
742
743
744
745
746
747
  	/*
  	 * We must ignore files (but only files) which might have mandatory
  	 * locks on them because there is no way to know if the accesser has
  	 * the lock.
  	 */
  	if (S_ISREG((inode)->i_mode) && mandatory_lock(inode))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
749
750
751
  		goto out;
  
  	if (!inode->i_fop)
  		goto out;
105f46221   J. Bruce Fields   nfsd4: fix break_...
752
  	host_err = nfsd_open_break_lease(inode, access);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
753
  	if (host_err) /* NOMEM or WOULDBLOCK */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
  		goto out_nfserr;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
755
756
  	if (access & NFSD_MAY_WRITE) {
  		if (access & NFSD_MAY_READ)
9ecb6a08d   J. Bruce Fields   [PATCH] knfsd: nf...
757
758
759
  			flags = O_RDWR|O_LARGEFILE;
  		else
  			flags = O_WRONLY|O_LARGEFILE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
760
  	}
547754916   Jan Blunck   Use struct path i...
761
  	*filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_path.mnt),
033a666cc   David Howells   NFSD: Don't hold ...
762
  			    flags, current_cred());
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
  	if (IS_ERR(*filp))
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
764
  		host_err = PTR_ERR(*filp);
aeaa5ccd6   Chuck Ebbert   vfs: don't call i...
765
766
  	else
  		host_err = ima_file_check(*filp, access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
768
  	err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
  out:
e0e817392   David Howells   CRED: Add some co...
770
  	validate_process_creds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
772
773
774
775
776
777
778
779
780
781
  	return err;
  }
  
  /*
   * Close a file.
   */
  void
  nfsd_close(struct file *filp)
  {
  	fput(filp);
  }
9a8d248e2   J. Bruce Fields   nfsd: fix double-...
782
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
784
785
   * Obtain the readahead parameters for the file
   * specified by (dev, ino).
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
787
788
789
790
791
  
  static inline struct raparms *
  nfsd_get_raparms(dev_t dev, ino_t ino)
  {
  	struct raparms	*ra, **rap, **frap = NULL;
  	int depth = 0;
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
792
793
794
795
796
  	unsigned int hash;
  	struct raparm_hbucket *rab;
  
  	hash = jhash_2words(dev, ino, 0xfeedbeef) & RAPARM_HASH_MASK;
  	rab = &raparm_hash[hash];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797

fce1456a1   Greg Banks   [PATCH] knfsd: ma...
798
799
  	spin_lock(&rab->pb_lock);
  	for (rap = &rab->pb_head; (ra = *rap); rap = &ra->p_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
803
804
805
  		if (ra->p_ino == ino && ra->p_dev == dev)
  			goto found;
  		depth++;
  		if (ra->p_count == 0)
  			frap = rap;
  	}
3aa6e0aa8   Konstantin Khorenko   NFSD: memory corr...
806
  	depth = nfsdstats.ra_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
  	if (!frap) {	
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
808
  		spin_unlock(&rab->pb_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809
810
811
812
813
814
815
  		return NULL;
  	}
  	rap = frap;
  	ra = *frap;
  	ra->p_dev = dev;
  	ra->p_ino = ino;
  	ra->p_set = 0;
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
816
  	ra->p_hindex = hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
  found:
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
818
  	if (rap != &rab->pb_head) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
  		*rap = ra->p_next;
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
820
821
  		ra->p_next   = rab->pb_head;
  		rab->pb_head = ra;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
  	}
  	ra->p_count++;
  	nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++;
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
825
  	spin_unlock(&rab->pb_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
828
829
  	return ra;
  }
  
  /*
cf8208d0e   Jens Axboe   sendfile: convert...
830
831
832
   * Grab and keep cached pages associated with a file in the svc_rqst
   * so that they can be passed to the network sendmsg/sendpage routines
   * directly. They will be released after the sending has completed.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
834
   */
  static int
cf8208d0e   Jens Axboe   sendfile: convert...
835
836
  nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
  		  struct splice_desc *sd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  {
cf8208d0e   Jens Axboe   sendfile: convert...
838
  	struct svc_rqst *rqstp = sd->u.data;
445243594   NeilBrown   [PATCH] knfsd: Re...
839
  	struct page **pp = rqstp->rq_respages + rqstp->rq_resused;
cf8208d0e   Jens Axboe   sendfile: convert...
840
841
  	struct page *page = buf->page;
  	size_t size;
cf8208d0e   Jens Axboe   sendfile: convert...
842
843
  
  	size = sd->len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
845
846
  
  	if (rqstp->rq_res.page_len == 0) {
  		get_page(page);
445243594   NeilBrown   [PATCH] knfsd: Re...
847
848
849
  		put_page(*pp);
  		*pp = page;
  		rqstp->rq_resused++;
cf8208d0e   Jens Axboe   sendfile: convert...
850
  		rqstp->rq_res.page_base = buf->offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
  		rqstp->rq_res.page_len = size;
445243594   NeilBrown   [PATCH] knfsd: Re...
852
  	} else if (page != pp[-1]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853
  		get_page(page);
250f39151   NeilBrown   [PATCH] knfsd: fi...
854
855
  		if (*pp)
  			put_page(*pp);
445243594   NeilBrown   [PATCH] knfsd: Re...
856
857
  		*pp = page;
  		rqstp->rq_resused++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  		rqstp->rq_res.page_len += size;
445243594   NeilBrown   [PATCH] knfsd: Re...
859
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
  		rqstp->rq_res.page_len += size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
  	return size;
  }
cf8208d0e   Jens Axboe   sendfile: convert...
864
865
866
867
868
  static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
  				    struct splice_desc *sd)
  {
  	return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
  }
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
869
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
870
871
872
  nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
                loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
  	mm_segment_t	oldfs;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
874
875
  	__be32		err;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
  
  	err = nfserr_perm;
a8754beed   Dave Hansen   r/o bind mounts: ...
878

cf8208d0e   Jens Axboe   sendfile: convert...
879
880
881
882
883
884
885
  	if (file->f_op->splice_read && rqstp->rq_splice_ok) {
  		struct splice_desc sd = {
  			.len		= 0,
  			.total_len	= *count,
  			.pos		= offset,
  			.u.data		= rqstp,
  		};
4fbef206d   Jens Axboe   nfsd: fix nfsd_vf...
886
  		rqstp->rq_resused = 1;
cf8208d0e   Jens Axboe   sendfile: convert...
887
  		host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
  	} else {
  		oldfs = get_fs();
  		set_fs(KERNEL_DS);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
891
  		host_err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
893
  		set_fs(oldfs);
  	}
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
894
895
896
  	if (host_err >= 0) {
  		nfsdstats.io_read += host_err;
  		*count = host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
  		err = 0;
2a12a9d78   Eric Paris   fsnotify: pass a ...
898
  		fsnotify_access(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
  	} else 
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
900
  		err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
902
  	return err;
  }
9f708e40f   Neil Brown   [PATCH] knfsd: re...
903
904
905
  static void kill_suid(struct dentry *dentry)
  {
  	struct iattr	ia;
b53767719   Serge E. Hallyn   Implement file po...
906
  	ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
9f708e40f   Neil Brown   [PATCH] knfsd: re...
907

1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
908
  	mutex_lock(&dentry->d_inode->i_mutex);
9f708e40f   Neil Brown   [PATCH] knfsd: re...
909
  	notify_change(dentry, &ia);
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
910
  	mutex_unlock(&dentry->d_inode->i_mutex);
9f708e40f   Neil Brown   [PATCH] knfsd: re...
911
  }
d911df7b8   J. Bruce Fields   nfsd: Pull write-...
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
  /*
   * Gathered writes: If another process is currently writing to the file,
   * there's a high chance this is another nfsd (triggered by a bulk write
   * from a client's biod). Rather than syncing the file with each write
   * request, we sleep for 10 msec.
   *
   * I don't know if this roughly approximates C. Juszak's idea of
   * gathered writes, but it's a nice and simple solution (IMHO), and it
   * seems to work:-)
   *
   * Note: we do this only in the NFSv2 case, since v3 and higher have a
   * better tool (separate unstable writes and commits) for solving this
   * problem.
   */
  static int wait_for_concurrent_writes(struct file *file)
  {
  	struct inode *inode = file->f_path.dentry->d_inode;
  	static ino_t last_ino;
  	static dev_t last_dev;
  	int err = 0;
  
  	if (atomic_read(&inode->i_writecount) > 1
  	    || (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) {
  		dprintk("nfsd: write defer %d
  ", task_pid_nr(current));
  		msleep(10);
  		dprintk("nfsd: write resume %d
  ", task_pid_nr(current));
  	}
  
  	if (inode->i_state & I_DIRTY) {
  		dprintk("nfsd: write sync %d
  ", task_pid_nr(current));
8018ab057   Christoph Hellwig   sanitize vfs_fsyn...
945
  		err = vfs_fsync(file, 0);
d911df7b8   J. Bruce Fields   nfsd: Pull write-...
946
947
948
949
950
  	}
  	last_ino = inode->i_ino;
  	last_dev = inode->i_sb->s_dev;
  	return err;
  }
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
951
  static __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
953
  nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
  				loff_t offset, struct kvec *vec, int vlen,
31dec2538   David Shaw   Short write in nf...
954
  				unsigned long *cnt, int *stablep)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
956
957
958
959
  {
  	struct svc_export	*exp;
  	struct dentry		*dentry;
  	struct inode		*inode;
  	mm_segment_t		oldfs;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
960
961
  	__be32			err = 0;
  	int			host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
  	int			stable = *stablep;
48e03bc51   Trond Myklebust   nfsd: Use write g...
963
  	int			use_wgather;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964

7eaa36e2d   Josef "Jeff" Sipek   [PATCH] nfsd: cha...
965
  	dentry = file->f_path.dentry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
967
968
969
970
971
972
973
  	inode = dentry->d_inode;
  	exp   = fhp->fh_export;
  
  	/*
  	 * Request sync writes if
  	 *  -	the sync export option has been set, or
  	 *  -	the client requested O_SYNC behavior (NFSv3 feature).
  	 *  -   The file system doesn't support fsync().
48e03bc51   Trond Myklebust   nfsd: Use write g...
974
  	 * When NFSv2 gathered writes have been configured for this volume,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
976
  	 * flushing the data to disk is handled separately below.
  	 */
48e03bc51   Trond Myklebust   nfsd: Use write g...
977
  	use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978

3ba151481   Harvey Harrison   nfsd: fix sparse ...
979
  	if (!file->f_op->fsync) {/* COMMIT3 cannot work */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
981
982
983
984
985
  	       stable = 2;
  	       *stablep = 2; /* FILE_SYNC */
  	}
  
  	if (!EX_ISSYNC(exp))
  		stable = 0;
48e03bc51   Trond Myklebust   nfsd: Use write g...
986
  	if (stable && !use_wgather) {
db1dd4d37   Jonathan Corbet   Use f_lock to pro...
987
  		spin_lock(&file->f_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
  		file->f_flags |= O_SYNC;
db1dd4d37   Jonathan Corbet   Use f_lock to pro...
989
990
  		spin_unlock(&file->f_lock);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
991
992
993
  
  	/* Write the data. */
  	oldfs = get_fs(); set_fs(KERNEL_DS);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
994
  	host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
  	set_fs(oldfs);
e4636d535   J. Bruce Fields   nfsd: minor nfsd_...
996
997
998
999
  	if (host_err < 0)
  		goto out_nfserr;
  	*cnt = host_err;
  	nfsdstats.io_write += host_err;
2a12a9d78   Eric Paris   fsnotify: pass a ...
1000
  	fsnotify_modify(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
  
  	/* clear setuid/setgid flag after write */
e4636d535   J. Bruce Fields   nfsd: minor nfsd_...
1003
  	if (inode->i_mode & (S_ISUID | S_ISGID))
9f708e40f   Neil Brown   [PATCH] knfsd: re...
1004
  		kill_suid(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005

e4636d535   J. Bruce Fields   nfsd: minor nfsd_...
1006
  	if (stable && use_wgather)
d911df7b8   J. Bruce Fields   nfsd: Pull write-...
1007
  		host_err = wait_for_concurrent_writes(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008

e4636d535   J. Bruce Fields   nfsd: minor nfsd_...
1009
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1010
1011
  	dprintk("nfsd: write complete host_err=%d
  ", host_err);
a0d24b295   Wei Yongjun   nfsd: fix hung up...
1012
  	if (host_err >= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
  		err = 0;
a0d24b295   Wei Yongjun   nfsd: fix hung up...
1014
  	else
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1015
  		err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
1017
1018
1019
1020
1021
1022
1023
  	return err;
  }
  
  /*
   * Read data from a file. count must contain the requested read count
   * on entry. On return, *count contains the number of bytes actually read.
   * N.B. After this call fhp needs an fh_put
   */
039a87ca5   J. Bruce Fields   nfsd: minor nfsd ...
1024
  __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
fa0a21269   J. Bruce Fields   nfsd: bypass read...
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
  	loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
  {
  	struct file *file;
  	struct inode *inode;
  	struct raparms	*ra;
  	__be32 err;
  
  	err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file);
  	if (err)
  		return err;
  
  	inode = file->f_path.dentry->d_inode;
  
  	/* Get readahead parameters */
  	ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino);
  
  	if (ra && ra->p_set)
  		file->f_ra = ra->p_ra;
  
  	err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count);
  
  	/* Write back readahead params */
  	if (ra) {
  		struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex];
  		spin_lock(&rab->pb_lock);
  		ra->p_ra = file->f_ra;
  		ra->p_set = 1;
  		ra->p_count--;
  		spin_unlock(&rab->pb_lock);
  	}
  
  	nfsd_close(file);
  	return err;
  }
039a87ca5   J. Bruce Fields   nfsd: minor nfsd ...
1059
  /* As above, but use the provided file descriptor. */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1060
  __be32
039a87ca5   J. Bruce Fields   nfsd: minor nfsd ...
1061
  nfsd_read_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
1063
1064
  		loff_t offset, struct kvec *vec, int vlen,
  		unsigned long *count)
  {
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1065
  	__be32		err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066
1067
  
  	if (file) {
0ec757df9   J. Bruce Fields   knfsd: nfsd4: mak...
1068
  		err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1069
  				NFSD_MAY_READ|NFSD_MAY_OWNER_OVERRIDE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
1072
  		if (err)
  			goto out;
  		err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count);
039a87ca5   J. Bruce Fields   nfsd: minor nfsd ...
1073
1074
  	} else /* Note file may still be NULL in NFSv4 special stateid case: */
  		err = nfsd_read(rqstp, fhp, offset, vec, vlen, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1075
1076
1077
1078
1079
1080
1081
1082
1083
  out:
  	return err;
  }
  
  /*
   * Write data to a file.
   * The stable flag requests synchronous writes.
   * N.B. After this call fhp needs an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1084
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
  nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
31dec2538   David Shaw   Short write in nf...
1086
  		loff_t offset, struct kvec *vec, int vlen, unsigned long *cnt,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087
1088
  		int *stablep)
  {
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1089
  	__be32			err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1090
1091
  
  	if (file) {
0ec757df9   J. Bruce Fields   knfsd: nfsd4: mak...
1092
  		err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1093
  				NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1094
1095
1096
1097
1098
  		if (err)
  			goto out;
  		err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, cnt,
  				stablep);
  	} else {
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1099
  		err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
  		if (err)
  			goto out;
  
  		if (cnt)
  			err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen,
  					     cnt, stablep);
  		nfsd_close(file);
  	}
  out:
  	return err;
  }
  
  #ifdef CONFIG_NFSD_V3
  /*
   * Commit all pending writes to stable storage.
aa696a6f3   Trond Myklebust   nfsd: Use vfs_fsy...
1115
1116
1117
   *
   * Note: we only guarantee that data that lies within the range specified
   * by the 'offset' and 'count' parameters will be synced.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1118
1119
1120
1121
   *
   * Unfortunately we cannot lock the file to make sure we return full WCC
   * data to the client, as locking happens lower down in the filesystem.
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1122
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1123
1124
1125
1126
  nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                 loff_t offset, unsigned long count)
  {
  	struct file	*file;
aa696a6f3   Trond Myklebust   nfsd: Use vfs_fsy...
1127
1128
  	loff_t		end = LLONG_MAX;
  	__be32		err = nfserr_inval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129

aa696a6f3   Trond Myklebust   nfsd: Use vfs_fsy...
1130
1131
1132
1133
1134
1135
1136
  	if (offset < 0)
  		goto out;
  	if (count != 0) {
  		end = offset + (loff_t)count - 1;
  		if (end < offset)
  			goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1137

91885258e   Jeff Layton   nfsd: don't break...
1138
1139
  	err = nfsd_open(rqstp, fhp, S_IFREG,
  			NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &file);
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1140
  	if (err)
aa696a6f3   Trond Myklebust   nfsd: Use vfs_fsy...
1141
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1142
  	if (EX_ISSYNC(fhp->fh_export)) {
8018ab057   Christoph Hellwig   sanitize vfs_fsyn...
1143
  		int err2 = vfs_fsync_range(file, offset, end, 0);
aa696a6f3   Trond Myklebust   nfsd: Use vfs_fsy...
1144
1145
1146
1147
  
  		if (err2 != -EINVAL)
  			err = nfserrno(err2);
  		else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1148
  			err = nfserr_notsupp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1149
1150
1151
  	}
  
  	nfsd_close(file);
aa696a6f3   Trond Myklebust   nfsd: Use vfs_fsy...
1152
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1153
1154
1155
  	return err;
  }
  #endif /* CONFIG_NFSD_V3 */
f2b0dee2e   Adrian Bunk   make nfsd_create_...
1156
  static __be32
5c002b3bb   J. Bruce Fields   nfsd: allow root ...
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
  nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp,
  			struct iattr *iap)
  {
  	/*
  	 * Mode has already been set earlier in create:
  	 */
  	iap->ia_valid &= ~ATTR_MODE;
  	/*
  	 * Setting uid/gid works only for root.  Irix appears to
  	 * send along the gid on create when it tries to implement
  	 * setgid directories via NFS:
  	 */
5cc0a8407   David Howells   CRED: Wrap task c...
1169
  	if (current_fsuid() != 0)
5c002b3bb   J. Bruce Fields   nfsd: allow root ...
1170
1171
1172
1173
1174
  		iap->ia_valid &= ~(ATTR_UID|ATTR_GID);
  	if (iap->ia_valid)
  		return nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0);
  	return 0;
  }
4ac35c2f7   wengang wang   nfsd(v2/v3): fix ...
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
  /* HPUX client sometimes creates a file in mode 000, and sets size to 0.
   * setting size to 0 may fail for some specific file systems by the permission
   * checking which requires WRITE permission but the mode is 000.
   * we ignore the resizing(to 0) on the just new created file, since the size is
   * 0 after file created.
   *
   * call this only after vfs_create() is called.
   * */
  static void
  nfsd_check_ignore_resizing(struct iattr *iap)
  {
  	if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
  		iap->ia_valid &= ~ATTR_SIZE;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1189
1190
1191
1192
1193
1194
1195
1196
  /*
   * Create a file (regular, directory, device, fifo); UNIX sockets 
   * not yet implemented.
   * If the response fh has been verified, the parent directory should
   * already be locked. Note that the parent directory is left locked.
   *
   * N.B. Every call to nfsd_create needs an fh_put for _both_ fhp and resfhp
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1197
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1198
1199
1200
1201
1202
1203
  nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
  		char *fname, int flen, struct iattr *iap,
  		int type, dev_t rdev, struct svc_fh *resfhp)
  {
  	struct dentry	*dentry, *dchild = NULL;
  	struct inode	*dirp;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1204
  	__be32		err;
5c002b3bb   J. Bruce Fields   nfsd: allow root ...
1205
  	__be32		err2;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1206
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207
1208
1209
1210
1211
1212
1213
  
  	err = nfserr_perm;
  	if (!flen)
  		goto out;
  	err = nfserr_exist;
  	if (isdotent(fname, flen))
  		goto out;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1214
  	err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1215
1216
1217
1218
1219
1220
1221
  	if (err)
  		goto out;
  
  	dentry = fhp->fh_dentry;
  	dirp = dentry->d_inode;
  
  	err = nfserr_notdir;
acfa4380e   Al Viro   inode->i_op is ne...
1222
  	if (!dirp->i_op->lookup)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1223
1224
1225
1226
1227
1228
1229
  		goto out;
  	/*
  	 * Check whether the response file handle has been verified yet.
  	 * If it has, the parent directory should already be locked.
  	 */
  	if (!resfhp->fh_dentry) {
  		/* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
12fd35203   Peter Zijlstra   [PATCH] nfsd: loc...
1230
  		fh_lock_nested(fhp, I_MUTEX_PARENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231
  		dchild = lookup_one_len(fname, dentry, flen);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1232
  		host_err = PTR_ERR(dchild);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
  		if (IS_ERR(dchild))
  			goto out_nfserr;
  		err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
  		if (err)
  			goto out;
  	} else {
  		/* called from nfsd_proc_create */
  		dchild = dget(resfhp->fh_dentry);
  		if (!fhp->fh_locked) {
  			/* not actually possible */
  			printk(KERN_ERR
  				"nfsd_create: parent %s/%s not locked!
  ",
  				dentry->d_parent->d_name.name,
  				dentry->d_name.name);
d75f2b9f5   Al Viro   [PATCH] nfsd/vfs....
1248
  			err = nfserr_io;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
  			goto out;
  		}
  	}
  	/*
  	 * Make sure the child dentry is still negative ...
  	 */
  	err = nfserr_exist;
  	if (dchild->d_inode) {
  		dprintk("nfsd_create: dentry %s/%s not negative!
  ",
  			dentry->d_name.name, dchild->d_name.name);
  		goto out; 
  	}
  
  	if (!(iap->ia_valid & ATTR_MODE))
  		iap->ia_mode = 0;
  	iap->ia_mode = (iap->ia_mode & S_IALLUGO) | type;
07cad1d2a   Miklos Szeredi   nfsd: clean up mn...
1266
1267
1268
1269
1270
1271
1272
  	err = nfserr_inval;
  	if (!S_ISREG(type) && !S_ISDIR(type) && !special_file(type)) {
  		printk(KERN_WARNING "nfsd: bad file type %o in nfsd_create
  ",
  		       type);
  		goto out;
  	}
bad0dcffc   Al Viro   new helpers: fh_{...
1273
  	host_err = fh_want_write(fhp);
07cad1d2a   Miklos Szeredi   nfsd: clean up mn...
1274
1275
  	if (host_err)
  		goto out_nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1276
1277
1278
  	/*
  	 * Get the dir op function pointer.
  	 */
088406bcf   J. Bruce Fields   [PATCH] nfsd: fix...
1279
  	err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1280
1281
  	switch (type) {
  	case S_IFREG:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1282
  		host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL);
4ac35c2f7   wengang wang   nfsd(v2/v3): fix ...
1283
1284
  		if (!host_err)
  			nfsd_check_ignore_resizing(iap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1285
1286
  		break;
  	case S_IFDIR:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1287
  		host_err = vfs_mkdir(dirp, dchild, iap->ia_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1288
1289
1290
1291
1292
  		break;
  	case S_IFCHR:
  	case S_IFBLK:
  	case S_IFIFO:
  	case S_IFSOCK:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1293
  		host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1295
  	}
463c31972   Dave Hansen   [PATCH] r/o bind ...
1296
  	if (host_err < 0) {
bad0dcffc   Al Viro   new helpers: fh_{...
1297
  		fh_drop_write(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1298
  		goto out_nfserr;
463c31972   Dave Hansen   [PATCH] r/o bind ...
1299
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1300

f501912a3   Ben Myers   commit_metadata e...
1301
  	err = nfsd_create_setattr(rqstp, resfhp, iap);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1302

f501912a3   Ben Myers   commit_metadata e...
1303
1304
1305
1306
1307
1308
  	/*
  	 * nfsd_setattr already committed the child.  Transactional filesystems
  	 * had a chance to commit changes for both parent and child
  	 * simultaneously making the following commit_metadata a noop.
  	 */
  	err2 = nfserrno(commit_metadata(fhp));
5c002b3bb   J. Bruce Fields   nfsd: allow root ...
1309
1310
  	if (err2)
  		err = err2;
bad0dcffc   Al Viro   new helpers: fh_{...
1311
  	fh_drop_write(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
  	/*
  	 * Update the file handle to get the new inode info.
  	 */
  	if (!err)
  		err = fh_update(resfhp);
  out:
  	if (dchild && !IS_ERR(dchild))
  		dput(dchild);
  	return err;
  
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1323
  	err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1324
1325
1326
1327
  	goto out;
  }
  
  #ifdef CONFIG_NFSD_V3
ac6721a13   Mi Jinlong   nfsd41: make sure...
1328
1329
1330
1331
1332
1333
  
  static inline int nfsd_create_is_exclusive(int createmode)
  {
  	return createmode == NFS3_CREATE_EXCLUSIVE
  	       || createmode == NFS4_CREATE_EXCLUSIVE4_1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1334
  /*
ac6721a13   Mi Jinlong   nfsd41: make sure...
1335
   * NFSv3 and NFSv4 version of nfsd_create
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1336
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1337
  __be32
ac6721a13   Mi Jinlong   nfsd41: make sure...
1338
  do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1339
1340
  		char *fname, int flen, struct iattr *iap,
  		struct svc_fh *resfhp, int createmode, u32 *verifier,
856121b2e   J. Bruce Fields   nfsd4: warn on op...
1341
  	        bool *truncp, bool *created)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342
1343
1344
  {
  	struct dentry	*dentry, *dchild = NULL;
  	struct inode	*dirp;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1345
1346
  	__be32		err;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1347
  	__u32		v_mtime=0, v_atime=0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1348
1349
1350
1351
1352
1353
1354
1355
1356
  
  	err = nfserr_perm;
  	if (!flen)
  		goto out;
  	err = nfserr_exist;
  	if (isdotent(fname, flen))
  		goto out;
  	if (!(iap->ia_valid & ATTR_MODE))
  		iap->ia_mode = 0;
1574dff89   Sachin Prabhu   Open with O_CREAT...
1357
  	err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1358
1359
1360
1361
1362
1363
1364
1365
1366
  	if (err)
  		goto out;
  
  	dentry = fhp->fh_dentry;
  	dirp = dentry->d_inode;
  
  	/* Get all the sanity checks out of the way before
  	 * we lock the parent. */
  	err = nfserr_notdir;
acfa4380e   Al Viro   inode->i_op is ne...
1367
  	if (!dirp->i_op->lookup)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1368
  		goto out;
12fd35203   Peter Zijlstra   [PATCH] nfsd: loc...
1369
  	fh_lock_nested(fhp, I_MUTEX_PARENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1370
1371
1372
1373
1374
  
  	/*
  	 * Compose the response file handle.
  	 */
  	dchild = lookup_one_len(fname, dentry, flen);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1375
  	host_err = PTR_ERR(dchild);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1376
1377
  	if (IS_ERR(dchild))
  		goto out_nfserr;
1574dff89   Sachin Prabhu   Open with O_CREAT...
1378
1379
1380
1381
1382
1383
  	/* If file doesn't exist, check for permissions to create one */
  	if (!dchild->d_inode) {
  		err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
  		if (err)
  			goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1384
1385
1386
  	err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
  	if (err)
  		goto out;
ac6721a13   Mi Jinlong   nfsd41: make sure...
1387
  	if (nfsd_create_is_exclusive(createmode)) {
c397852c3   Peter Staubach   [PATCH] knfsd: Do...
1388
  		/* solaris7 gets confused (bugid 4218508) if these have
749997e51   Jeff Layton   knfsd: set the re...
1389
1390
1391
1392
  		 * the high bit set, so just clear the high bits. If this is
  		 * ever changed to use different attrs for storing the
  		 * verifier, then do_open_lookup() will also need to be fixed
  		 * accordingly.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1393
1394
1395
  		 */
  		v_mtime = verifier[0]&0x7fffffff;
  		v_atime = verifier[1]&0x7fffffff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1396
1397
  	}
  	
bad0dcffc   Al Viro   new helpers: fh_{...
1398
  	host_err = fh_want_write(fhp);
463c31972   Dave Hansen   [PATCH] r/o bind ...
1399
1400
  	if (host_err)
  		goto out_nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
  	if (dchild->d_inode) {
  		err = 0;
  
  		switch (createmode) {
  		case NFS3_CREATE_UNCHECKED:
  			if (! S_ISREG(dchild->d_inode->i_mode))
  				err = nfserr_exist;
  			else if (truncp) {
  				/* in nfsv4, we need to treat this case a little
  				 * differently.  we don't want to truncate the
  				 * file now; this would be wrong if the OPEN
  				 * fails for some other reason.  furthermore,
  				 * if the size is nonzero, we should ignore it
  				 * according to spec!
  				 */
  				*truncp = (iap->ia_valid & ATTR_SIZE) && !iap->ia_size;
  			}
  			else {
  				iap->ia_valid &= ATTR_SIZE;
  				goto set_attr;
  			}
  			break;
  		case NFS3_CREATE_EXCLUSIVE:
  			if (   dchild->d_inode->i_mtime.tv_sec == v_mtime
  			    && dchild->d_inode->i_atime.tv_sec == v_atime
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1426
1427
  			    && dchild->d_inode->i_size  == 0 )
  				break;
ac6721a13   Mi Jinlong   nfsd41: make sure...
1428
1429
1430
1431
1432
  		case NFS4_CREATE_EXCLUSIVE4_1:
  			if (   dchild->d_inode->i_mtime.tv_sec == v_mtime
  			    && dchild->d_inode->i_atime.tv_sec == v_atime
  			    && dchild->d_inode->i_size  == 0 )
  				goto set_attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
1434
1435
1436
  			 /* fallthru */
  		case NFS3_CREATE_GUARDED:
  			err = nfserr_exist;
  		}
bad0dcffc   Al Viro   new helpers: fh_{...
1437
  		fh_drop_write(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1438
1439
  		goto out;
  	}
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1440
  	host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL);
463c31972   Dave Hansen   [PATCH] r/o bind ...
1441
  	if (host_err < 0) {
bad0dcffc   Al Viro   new helpers: fh_{...
1442
  		fh_drop_write(fhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1443
  		goto out_nfserr;
463c31972   Dave Hansen   [PATCH] r/o bind ...
1444
  	}
81ac95c55   J. Bruce Fields   [PATCH] nfsd4: fi...
1445
1446
  	if (created)
  		*created = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1447

4ac35c2f7   wengang wang   nfsd(v2/v3): fix ...
1448
  	nfsd_check_ignore_resizing(iap);
ac6721a13   Mi Jinlong   nfsd41: make sure...
1449
  	if (nfsd_create_is_exclusive(createmode)) {
c397852c3   Peter Staubach   [PATCH] knfsd: Do...
1450
  		/* Cram the verifier into atime/mtime */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1451
  		iap->ia_valid = ATTR_MTIME|ATTR_ATIME
c397852c3   Peter Staubach   [PATCH] knfsd: Do...
1452
  			| ATTR_MTIME_SET|ATTR_ATIME_SET;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1453
1454
1455
1456
1457
  		/* XXX someone who knows this better please fix it for nsec */ 
  		iap->ia_mtime.tv_sec = v_mtime;
  		iap->ia_atime.tv_sec = v_atime;
  		iap->ia_mtime.tv_nsec = 0;
  		iap->ia_atime.tv_nsec = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1458
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1459
   set_attr:
f501912a3   Ben Myers   commit_metadata e...
1460
1461
1462
1463
1464
1465
1466
  	err = nfsd_create_setattr(rqstp, resfhp, iap);
  
  	/*
  	 * nfsd_setattr already committed the child (and possibly also the parent).
  	 */
  	if (!err)
  		err = nfserrno(commit_metadata(fhp));
f193fbab2   YAMAMOTO Takashi   [PATCH] nfsd: che...
1467

bad0dcffc   Al Viro   new helpers: fh_{...
1468
  	fh_drop_write(fhp);
f193fbab2   YAMAMOTO Takashi   [PATCH] nfsd: che...
1469
1470
1471
1472
1473
  	/*
  	 * Update the filehandle to get the new inode info.
  	 */
  	if (!err)
  		err = fh_update(resfhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1474
1475
1476
1477
1478
1479
1480
1481
  
   out:
  	fh_unlock(fhp);
  	if (dchild && !IS_ERR(dchild))
  		dput(dchild);
   	return err;
   
   out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1482
  	err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1483
1484
1485
1486
1487
1488
1489
1490
1491
  	goto out;
  }
  #endif /* CONFIG_NFSD_V3 */
  
  /*
   * Read a symlink. On entry, *lenp must contain the maximum path length that
   * fits into the buffer. On return, it contains the true length.
   * N.B. After this call fhp needs an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1492
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1493
1494
1495
1496
1497
  nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
  {
  	struct dentry	*dentry;
  	struct inode	*inode;
  	mm_segment_t	oldfs;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1498
1499
  	__be32		err;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1500

8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1501
  	err = fh_verify(rqstp, fhp, S_IFLNK, NFSD_MAY_NOP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1502
1503
1504
1505
1506
1507
1508
  	if (err)
  		goto out;
  
  	dentry = fhp->fh_dentry;
  	inode = dentry->d_inode;
  
  	err = nfserr_inval;
acfa4380e   Al Viro   inode->i_op is ne...
1509
  	if (!inode->i_op->readlink)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1510
  		goto out;
547754916   Jan Blunck   Use struct path i...
1511
  	touch_atime(fhp->fh_export->ex_path.mnt, dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1512
1513
1514
1515
1516
  	/* N.B. Why does this call need a get_fs()??
  	 * Remove the set_fs and watch the fireworks:-) --okir
  	 */
  
  	oldfs = get_fs(); set_fs(KERNEL_DS);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1517
  	host_err = inode->i_op->readlink(dentry, buf, *lenp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1518
  	set_fs(oldfs);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1519
  	if (host_err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1520
  		goto out_nfserr;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1521
  	*lenp = host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1522
1523
1524
1525
1526
  	err = 0;
  out:
  	return err;
  
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1527
  	err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1528
1529
1530
1531
1532
1533
1534
  	goto out;
  }
  
  /*
   * Create a symlink and look up its inode
   * N.B. After this call _both_ fhp and resfhp need an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1535
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1536
1537
1538
1539
1540
1541
1542
  nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
  				char *fname, int flen,
  				char *path,  int plen,
  				struct svc_fh *resfhp,
  				struct iattr *iap)
  {
  	struct dentry	*dentry, *dnew;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1543
1544
  	__be32		err, cerr;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1545
1546
1547
1548
1549
1550
1551
  
  	err = nfserr_noent;
  	if (!flen || !plen)
  		goto out;
  	err = nfserr_exist;
  	if (isdotent(fname, flen))
  		goto out;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1552
  	err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1553
1554
1555
1556
1557
  	if (err)
  		goto out;
  	fh_lock(fhp);
  	dentry = fhp->fh_dentry;
  	dnew = lookup_one_len(fname, dentry, flen);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1558
  	host_err = PTR_ERR(dnew);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1559
1560
  	if (IS_ERR(dnew))
  		goto out_nfserr;
bad0dcffc   Al Viro   new helpers: fh_{...
1561
  	host_err = fh_want_write(fhp);
75c3f29de   Dave Hansen   [PATCH] r/o bind ...
1562
1563
  	if (host_err)
  		goto out_nfserr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1564
1565
1566
  	if (unlikely(path[plen] != 0)) {
  		char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
  		if (path_alloced == NULL)
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1567
  			host_err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1568
1569
1570
  		else {
  			strncpy(path_alloced, path, plen);
  			path_alloced[plen] = 0;
db2e747b1   Miklos Szeredi   [patch 5/5] vfs: ...
1571
  			host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1572
1573
1574
  			kfree(path_alloced);
  		}
  	} else
db2e747b1   Miklos Szeredi   [patch 5/5] vfs: ...
1575
  		host_err = vfs_symlink(dentry->d_inode, dnew, path);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1576
  	err = nfserrno(host_err);
f501912a3   Ben Myers   commit_metadata e...
1577
1578
  	if (!err)
  		err = nfserrno(commit_metadata(fhp));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1579
  	fh_unlock(fhp);
bad0dcffc   Al Viro   new helpers: fh_{...
1580
  	fh_drop_write(fhp);
75c3f29de   Dave Hansen   [PATCH] r/o bind ...
1581

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1582
1583
1584
1585
1586
1587
1588
  	cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
  	dput(dnew);
  	if (err==0) err = cerr;
  out:
  	return err;
  
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1589
  	err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1590
1591
1592
1593
1594
1595
1596
  	goto out;
  }
  
  /*
   * Create a hardlink
   * N.B. After this call _both_ ffhp and tfhp need an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1597
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1598
1599
1600
1601
  nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
  				char *name, int len, struct svc_fh *tfhp)
  {
  	struct dentry	*ddir, *dnew, *dold;
55b13354d   J. Bruce Fields   nfsd: remove unus...
1602
  	struct inode	*dirp;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1603
1604
  	__be32		err;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1605

8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1606
  	err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1607
1608
  	if (err)
  		goto out;
7d818a7b8   J. Bruce Fields   nfsd: open-code s...
1609
  	err = fh_verify(rqstp, tfhp, 0, NFSD_MAY_NOP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1610
1611
  	if (err)
  		goto out;
7d818a7b8   J. Bruce Fields   nfsd: open-code s...
1612
1613
1614
  	err = nfserr_isdir;
  	if (S_ISDIR(tfhp->fh_dentry->d_inode->i_mode))
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1615
1616
1617
1618
1619
1620
  	err = nfserr_perm;
  	if (!len)
  		goto out;
  	err = nfserr_exist;
  	if (isdotent(name, len))
  		goto out;
12fd35203   Peter Zijlstra   [PATCH] nfsd: loc...
1621
  	fh_lock_nested(ffhp, I_MUTEX_PARENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1622
1623
1624
1625
  	ddir = ffhp->fh_dentry;
  	dirp = ddir->d_inode;
  
  	dnew = lookup_one_len(name, ddir, len);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1626
  	host_err = PTR_ERR(dnew);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1627
1628
1629
1630
  	if (IS_ERR(dnew))
  		goto out_nfserr;
  
  	dold = tfhp->fh_dentry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1631

bad0dcffc   Al Viro   new helpers: fh_{...
1632
  	host_err = fh_want_write(tfhp);
75c3f29de   Dave Hansen   [PATCH] r/o bind ...
1633
1634
1635
1636
  	if (host_err) {
  		err = nfserrno(host_err);
  		goto out_dput;
  	}
4795bb37e   J. Bruce Fields   nfsd: break lease...
1637
1638
1639
1640
  	err = nfserr_noent;
  	if (!dold->d_inode)
  		goto out_drop_write;
  	host_err = nfsd_break_lease(dold->d_inode);
7d751f6f8   Casey Bodley   nfsd: link return...
1641
1642
  	if (host_err) {
  		err = nfserrno(host_err);
4795bb37e   J. Bruce Fields   nfsd: break lease...
1643
  		goto out_drop_write;
7d751f6f8   Casey Bodley   nfsd: link return...
1644
  	}
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1645
1646
  	host_err = vfs_link(dold, dirp, dnew);
  	if (!host_err) {
f501912a3   Ben Myers   commit_metadata e...
1647
1648
1649
  		err = nfserrno(commit_metadata(ffhp));
  		if (!err)
  			err = nfserrno(commit_metadata(tfhp));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1650
  	} else {
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1651
  		if (host_err == -EXDEV && rqstp->rq_vers == 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1652
1653
  			err = nfserr_acces;
  		else
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1654
  			err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1655
  	}
4795bb37e   J. Bruce Fields   nfsd: break lease...
1656
  out_drop_write:
bad0dcffc   Al Viro   new helpers: fh_{...
1657
  	fh_drop_write(tfhp);
75c3f29de   Dave Hansen   [PATCH] r/o bind ...
1658
  out_dput:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1659
  	dput(dnew);
270d56e53   David M. Richter   [PATCH] knfsd: nf...
1660
1661
  out_unlock:
  	fh_unlock(ffhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1662
1663
1664
1665
  out:
  	return err;
  
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1666
  	err = nfserrno(host_err);
270d56e53   David M. Richter   [PATCH] knfsd: nf...
1667
  	goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1668
1669
1670
1671
1672
1673
  }
  
  /*
   * Rename a file
   * N.B. After this call _both_ ffhp and tfhp need an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1674
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1675
1676
1677
1678
1679
  nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
  			    struct svc_fh *tfhp, char *tname, int tlen)
  {
  	struct dentry	*fdentry, *tdentry, *odentry, *ndentry, *trap;
  	struct inode	*fdir, *tdir;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1680
1681
  	__be32		err;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1682

8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1683
  	err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1684
1685
  	if (err)
  		goto out;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1686
  	err = fh_verify(rqstp, tfhp, S_IFDIR, NFSD_MAY_CREATE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
  	if (err)
  		goto out;
  
  	fdentry = ffhp->fh_dentry;
  	fdir = fdentry->d_inode;
  
  	tdentry = tfhp->fh_dentry;
  	tdir = tdentry->d_inode;
  
  	err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
a56f39375   NeilBrown   [PATCH] knfsd: im...
1697
  	if (ffhp->fh_export != tfhp->fh_export)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
  		goto out;
  
  	err = nfserr_perm;
  	if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
  		goto out;
  
  	/* cannot use fh_lock as we need deadlock protective ordering
  	 * so do it by hand */
  	trap = lock_rename(tdentry, fdentry);
  	ffhp->fh_locked = tfhp->fh_locked = 1;
  	fill_pre_wcc(ffhp);
  	fill_pre_wcc(tfhp);
  
  	odentry = lookup_one_len(fname, fdentry, flen);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1712
  	host_err = PTR_ERR(odentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1713
1714
  	if (IS_ERR(odentry))
  		goto out_nfserr;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1715
  	host_err = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1716
1717
  	if (!odentry->d_inode)
  		goto out_dput_old;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1718
  	host_err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1719
1720
1721
1722
  	if (odentry == trap)
  		goto out_dput_old;
  
  	ndentry = lookup_one_len(tname, tdentry, tlen);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1723
  	host_err = PTR_ERR(ndentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1724
1725
  	if (IS_ERR(ndentry))
  		goto out_dput_old;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1726
  	host_err = -ENOTEMPTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1727
1728
  	if (ndentry == trap)
  		goto out_dput_new;
9079b1eb1   Dave Hansen   [PATCH] r/o bind ...
1729
1730
1731
  	host_err = -EXDEV;
  	if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt)
  		goto out_dput_new;
bad0dcffc   Al Viro   new helpers: fh_{...
1732
  	host_err = fh_want_write(ffhp);
9079b1eb1   Dave Hansen   [PATCH] r/o bind ...
1733
1734
  	if (host_err)
  		goto out_dput_new;
4795bb37e   J. Bruce Fields   nfsd: break lease...
1735
1736
1737
  	host_err = nfsd_break_lease(odentry->d_inode);
  	if (host_err)
  		goto out_drop_write;
83f6b0c18   J. Bruce Fields   nfsd: break lease...
1738
1739
1740
1741
1742
  	if (ndentry->d_inode) {
  		host_err = nfsd_break_lease(ndentry->d_inode);
  		if (host_err)
  			goto out_drop_write;
  	}
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1743
  	host_err = vfs_rename(fdir, odentry, tdir, ndentry);
f501912a3   Ben Myers   commit_metadata e...
1744
1745
  	if (!host_err) {
  		host_err = commit_metadata(tfhp);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1746
  		if (!host_err)
f501912a3   Ben Myers   commit_metadata e...
1747
  			host_err = commit_metadata(ffhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1748
  	}
4795bb37e   J. Bruce Fields   nfsd: break lease...
1749
  out_drop_write:
bad0dcffc   Al Viro   new helpers: fh_{...
1750
  	fh_drop_write(ffhp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1751
1752
1753
1754
1755
   out_dput_new:
  	dput(ndentry);
   out_dput_old:
  	dput(odentry);
   out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1756
  	err = nfserrno(host_err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
  
  	/* we cannot reply on fh_unlock on the two filehandles,
  	 * as that would do the wrong thing if the two directories
  	 * were the same, so again we do it by hand
  	 */
  	fill_post_wcc(ffhp);
  	fill_post_wcc(tfhp);
  	unlock_rename(tdentry, fdentry);
  	ffhp->fh_locked = tfhp->fh_locked = 0;
  
  out:
  	return err;
  }
  
  /*
   * Unlink a file or directory
   * N.B. After this call fhp needs an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1775
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1776
1777
1778
1779
1780
  nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
  				char *fname, int flen)
  {
  	struct dentry	*dentry, *rdentry;
  	struct inode	*dirp;
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1781
1782
  	__be32		err;
  	int		host_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1783
1784
1785
1786
  
  	err = nfserr_acces;
  	if (!flen || isdotent(fname, flen))
  		goto out;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1787
  	err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_REMOVE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1788
1789
  	if (err)
  		goto out;
12fd35203   Peter Zijlstra   [PATCH] nfsd: loc...
1790
  	fh_lock_nested(fhp, I_MUTEX_PARENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1791
1792
1793
1794
  	dentry = fhp->fh_dentry;
  	dirp = dentry->d_inode;
  
  	rdentry = lookup_one_len(fname, dentry, flen);
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1795
  	host_err = PTR_ERR(rdentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
  	if (IS_ERR(rdentry))
  		goto out_nfserr;
  
  	if (!rdentry->d_inode) {
  		dput(rdentry);
  		err = nfserr_noent;
  		goto out;
  	}
  
  	if (!type)
  		type = rdentry->d_inode->i_mode & S_IFMT;
bad0dcffc   Al Viro   new helpers: fh_{...
1807
  	host_err = fh_want_write(fhp);
0622753b8   Dave Hansen   [PATCH] r/o bind ...
1808
  	if (host_err)
541ce98c1   J. Bruce Fields   nfsd: don't leak ...
1809
  		goto out_put;
0622753b8   Dave Hansen   [PATCH] r/o bind ...
1810

4795bb37e   J. Bruce Fields   nfsd: break lease...
1811
1812
  	host_err = nfsd_break_lease(rdentry->d_inode);
  	if (host_err)
541ce98c1   J. Bruce Fields   nfsd: don't leak ...
1813
  		goto out_drop_write;
9ce137eee   J. Bruce Fields   nfsd: don't suppo...
1814
  	if (type != S_IFDIR)
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1815
  		host_err = vfs_unlink(dirp, rdentry);
9ce137eee   J. Bruce Fields   nfsd: don't suppo...
1816
  	else
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1817
  		host_err = vfs_rmdir(dirp, rdentry);
f501912a3   Ben Myers   commit_metadata e...
1818
1819
  	if (!host_err)
  		host_err = commit_metadata(fhp);
541ce98c1   J. Bruce Fields   nfsd: don't leak ...
1820
  out_drop_write:
bad0dcffc   Al Viro   new helpers: fh_{...
1821
  	fh_drop_write(fhp);
541ce98c1   J. Bruce Fields   nfsd: don't leak ...
1822
1823
  out_put:
  	dput(rdentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1824
  out_nfserr:
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1825
  	err = nfserrno(host_err);
f193fbab2   YAMAMOTO Takashi   [PATCH] nfsd: che...
1826
1827
  out:
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1828
1829
1830
  }
  
  /*
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
   * We do this buffering because we must not call back into the file
   * system's ->lookup() method from the filldir callback. That may well
   * deadlock a number of file systems.
   *
   * This is based heavily on the implementation of same in XFS.
   */
  struct buffered_dirent {
  	u64		ino;
  	loff_t		offset;
  	int		namlen;
  	unsigned int	d_type;
  	char		name[];
  };
  
  struct readdir_data {
  	char		*dirent;
  	size_t		used;
53c9c5c0e   Al Viro   [PATCH] prepare v...
1848
  	int		full;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
  };
  
  static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
  				 loff_t offset, u64 ino, unsigned int d_type)
  {
  	struct readdir_data *buf = __buf;
  	struct buffered_dirent *de = (void *)(buf->dirent + buf->used);
  	unsigned int reclen;
  
  	reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64));
53c9c5c0e   Al Viro   [PATCH] prepare v...
1859
1860
  	if (buf->used + reclen > PAGE_SIZE) {
  		buf->full = 1;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1861
  		return -EINVAL;
53c9c5c0e   Al Viro   [PATCH] prepare v...
1862
  	}
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
  
  	de->namlen = namlen;
  	de->offset = offset;
  	de->ino = ino;
  	de->d_type = d_type;
  	memcpy(de->name, name, namlen);
  	buf->used += reclen;
  
  	return 0;
  }
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
1873
1874
  static __be32 nfsd_buffered_readdir(struct file *file, filldir_t func,
  				    struct readdir_cd *cdp, loff_t *offsetp)
2628b7663   David Woodhouse   [PATCH] Factor ou...
1875
  {
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1876
1877
  	struct readdir_data buf;
  	struct buffered_dirent *de;
2628b7663   David Woodhouse   [PATCH] Factor ou...
1878
  	int host_err;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1879
1880
  	int size;
  	loff_t offset;
2628b7663   David Woodhouse   [PATCH] Factor ou...
1881

14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1882
1883
  	buf.dirent = (void *)__get_free_page(GFP_KERNEL);
  	if (!buf.dirent)
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
1884
  		return nfserrno(-ENOMEM);
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1885
1886
  
  	offset = *offsetp;
2628b7663   David Woodhouse   [PATCH] Factor ou...
1887

14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1888
  	while (1) {
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
1889
  		struct inode *dir_inode = file->f_path.dentry->d_inode;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1890
  		unsigned int reclen;
b726e923e   Doug Nazar   Fix nfsd truncati...
1891
  		cdp->err = nfserr_eof; /* will be cleared on successful read */
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1892
  		buf.used = 0;
53c9c5c0e   Al Viro   [PATCH] prepare v...
1893
  		buf.full = 0;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1894
1895
  
  		host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf);
53c9c5c0e   Al Viro   [PATCH] prepare v...
1896
1897
1898
1899
  		if (buf.full)
  			host_err = 0;
  
  		if (host_err < 0)
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1900
1901
1902
1903
1904
1905
  			break;
  
  		size = buf.used;
  
  		if (!size)
  			break;
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
1906
1907
1908
1909
1910
1911
1912
1913
  		/*
  		 * Various filldir functions may end up calling back into
  		 * lookup_one_len() and the file system's ->lookup() method.
  		 * These expect i_mutex to be held, as it would within readdir.
  		 */
  		host_err = mutex_lock_killable(&dir_inode->i_mutex);
  		if (host_err)
  			break;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1914
1915
1916
1917
1918
1919
  		de = (struct buffered_dirent *)buf.dirent;
  		while (size > 0) {
  			offset = de->offset;
  
  			if (func(cdp, de->name, de->namlen, de->offset,
  				 de->ino, de->d_type))
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
1920
  				break;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1921
1922
  
  			if (cdp->err != nfs_ok)
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
1923
  				break;
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1924
1925
1926
1927
1928
1929
  
  			reclen = ALIGN(sizeof(*de) + de->namlen,
  				       sizeof(u64));
  			size -= reclen;
  			de = (struct buffered_dirent *)((char *)de + reclen);
  		}
2f9092e10   David Woodhouse   Fix i_mutex vs. r...
1930
1931
1932
  		mutex_unlock(&dir_inode->i_mutex);
  		if (size > 0) /* We bailed out early */
  			break;
c002a6c79   David Woodhouse   [PATCH] Optimise ...
1933
  		offset = vfs_llseek(file, 0, SEEK_CUR);
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1934
  	}
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1935
  	free_page((unsigned long)(buf.dirent));
2628b7663   David Woodhouse   [PATCH] Factor ou...
1936
1937
1938
  
  	if (host_err)
  		return nfserrno(host_err);
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1939
1940
1941
  
  	*offsetp = offset;
  	return cdp->err;
2628b7663   David Woodhouse   [PATCH] Factor ou...
1942
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1943
1944
1945
1946
  /*
   * Read entries from a directory.
   * The  NFSv3/4 verifier we ignore for now.
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1947
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1948
  nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, 
a0ad13ef6   NeilBrown   [PATCH] knfsd: Fi...
1949
  	     struct readdir_cd *cdp, filldir_t func)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1950
  {
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1951
  	__be32		err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1952
1953
  	struct file	*file;
  	loff_t		offset = *offsetp;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
1954
  	err = nfsd_open(rqstp, fhp, S_IFDIR, NFSD_MAY_READ, &file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1955
1956
1957
1958
1959
1960
1961
1962
  	if (err)
  		goto out;
  
  	offset = vfs_llseek(file, offset, 0);
  	if (offset < 0) {
  		err = nfserrno((int)offset);
  		goto out_close;
  	}
14f7dd632   David Woodhouse   [PATCH] Copy XFS ...
1963
  	err = nfsd_buffered_readdir(file, func, cdp, offsetp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
  
  	if (err == nfserr_eof || err == nfserr_toosmall)
  		err = nfs_ok; /* can still be found in ->err */
  out_close:
  	nfsd_close(file);
  out:
  	return err;
  }
  
  /*
   * Get file system stats
   * N.B. After this call fhp needs an fh_put
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
1977
  __be32
04716e662   J. Bruce Fields   nfsd: permit unau...
1978
  nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, int access)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1979
  {
ebabe9a90   Christoph Hellwig   pass a struct pat...
1980
1981
1982
  	__be32 err;
  
  	err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP | access);
f6360efb8   Takashi Iwai   nfsd: fix NULL de...
1983
1984
1985
1986
1987
1988
1989
1990
  	if (!err) {
  		struct path path = {
  			.mnt	= fhp->fh_export->ex_path.mnt,
  			.dentry	= fhp->fh_dentry,
  		};
  		if (vfs_statfs(&path, stat))
  			err = nfserr_io;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1991
1992
  	return err;
  }
c7d51402d   J. Bruce Fields   knfsd: clean up E...
1993
  static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp)
e22841c63   J. Bruce Fields   knfsd: move EX_RD...
1994
  {
c7d51402d   J. Bruce Fields   knfsd: clean up E...
1995
  	return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY;
e22841c63   J. Bruce Fields   knfsd: move EX_RD...
1996
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1997
1998
1999
  /*
   * Check for a user's access permissions to this inode.
   */
6264d69d7   Al Viro   [PATCH] nfsd: vfs...
2000
  __be32
0ec757df9   J. Bruce Fields   knfsd: nfsd4: mak...
2001
2002
  nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
  					struct dentry *dentry, int acc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2003
2004
2005
  {
  	struct inode	*inode = dentry->d_inode;
  	int		err;
aea93397d   J. Bruce Fields   nfsd: distinguish...
2006
  	if ((acc & NFSD_MAY_MASK) == NFSD_MAY_NOP)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2007
2008
2009
2010
2011
  		return 0;
  #if 0
  	dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s
  ",
  		acc,
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2012
2013
2014
2015
2016
2017
2018
  		(acc & NFSD_MAY_READ)?	" read"  : "",
  		(acc & NFSD_MAY_WRITE)?	" write" : "",
  		(acc & NFSD_MAY_EXEC)?	" exec"  : "",
  		(acc & NFSD_MAY_SATTR)?	" sattr" : "",
  		(acc & NFSD_MAY_TRUNC)?	" trunc" : "",
  		(acc & NFSD_MAY_LOCK)?	" lock"  : "",
  		(acc & NFSD_MAY_OWNER_OVERRIDE)? " owneroverride" : "",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2019
2020
2021
  		inode->i_mode,
  		IS_IMMUTABLE(inode)?	" immut" : "",
  		IS_APPEND(inode)?	" append" : "",
2c463e954   Dave Hansen   [PATCH] r/o bind ...
2022
  		__mnt_is_readonly(exp->ex_path.mnt)?	" ro" : "");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2023
2024
  	dprintk("      owner %d/%d user %d/%d
  ",
5cc0a8407   David Howells   CRED: Wrap task c...
2025
  		inode->i_uid, inode->i_gid, current_fsuid(), current_fsgid());
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2026
2027
2028
2029
2030
2031
  #endif
  
  	/* Normally we reject any write/sattr etc access on a read-only file
  	 * system.  But if it is IRIX doing check on write-access for a 
  	 * device special file, we ignore rofs.
  	 */
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2032
2033
  	if (!(acc & NFSD_MAY_LOCAL_ACCESS))
  		if (acc & (NFSD_MAY_WRITE | NFSD_MAY_SATTR | NFSD_MAY_TRUNC)) {
2c463e954   Dave Hansen   [PATCH] r/o bind ...
2034
2035
  			if (exp_rdonly(rqstp, exp) ||
  			    __mnt_is_readonly(exp->ex_path.mnt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2036
  				return nfserr_rofs;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2037
  			if (/* (acc & NFSD_MAY_WRITE) && */ IS_IMMUTABLE(inode))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2038
2039
  				return nfserr_perm;
  		}
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2040
  	if ((acc & NFSD_MAY_TRUNC) && IS_APPEND(inode))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2041
  		return nfserr_perm;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2042
  	if (acc & NFSD_MAY_LOCK) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2043
2044
2045
2046
2047
2048
2049
  		/* If we cannot rely on authentication in NLM requests,
  		 * just allow locks, otherwise require read permission, or
  		 * ownership
  		 */
  		if (exp->ex_flags & NFSEXP_NOAUTHNLM)
  			return 0;
  		else
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2050
  			acc = NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
  	}
  	/*
  	 * The file owner always gets access permission for accesses that
  	 * would normally be checked at open time. This is to make
  	 * file access work even when the client has done a fchmod(fd, 0).
  	 *
  	 * However, `cp foo bar' should fail nevertheless when bar is
  	 * readonly. A sensible way to do this might be to reject all
  	 * attempts to truncate a read-only file, because a creat() call
  	 * always implies file truncation.
  	 * ... but this isn't really fair.  A process may reasonably call
  	 * ftruncate on an open file descriptor on a file with perm 000.
  	 * We must trust the client to do permission checking - using "ACCESS"
  	 * with NFSv3.
  	 */
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2066
  	if ((acc & NFSD_MAY_OWNER_OVERRIDE) &&
5cc0a8407   David Howells   CRED: Wrap task c...
2067
  	    inode->i_uid == current_fsuid())
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2068
  		return 0;
8837abcab   Miklos Szeredi   nfsd: rename MAY_...
2069
  	/* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
f419a2e3b   Al Viro   [PATCH] kill name...
2070
  	err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2071
2072
2073
  
  	/* Allow read access to binaries even when mode 111 */
  	if (err == -EACCES && S_ISREG(inode->i_mode) &&
a043226bc   J. Bruce Fields   nfsd4: permit rea...
2074
2075
  	     (acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE) ||
  	      acc == (NFSD_MAY_READ | NFSD_MAY_READ_IF_EXEC)))
f419a2e3b   Al Viro   [PATCH] kill name...
2076
  		err = inode_permission(inode, MAY_EXEC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2077
2078
2079
2080
2081
2082
2083
  
  	return err? nfserrno(err) : 0;
  }
  
  void
  nfsd_racache_shutdown(void)
  {
54a66e548   Jeff Layton   knfsd: allocate r...
2084
2085
  	struct raparms *raparm, *last_raparm;
  	unsigned int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2086
2087
  	dprintk("nfsd: freeing readahead buffers.
  ");
54a66e548   Jeff Layton   knfsd: allocate r...
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
  
  	for (i = 0; i < RAPARM_HASH_SIZE; i++) {
  		raparm = raparm_hash[i].pb_head;
  		while(raparm) {
  			last_raparm = raparm;
  			raparm = raparm->p_next;
  			kfree(last_raparm);
  		}
  		raparm_hash[i].pb_head = NULL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2098
2099
2100
2101
2102
2103
2104
2105
  }
  /*
   * Initialize readahead param cache
   */
  int
  nfsd_racache_init(int cache_size)
  {
  	int	i;
fce1456a1   Greg Banks   [PATCH] knfsd: ma...
2106
2107
  	int	j = 0;
  	int	nperbucket;
54a66e548   Jeff Layton   knfsd: allocate r...
2108
  	struct raparms **raparm = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2109

fce1456a1   Greg Banks   [PATCH] knfsd: ma...
2110

54a66e548   Jeff Layton   knfsd: allocate r...
2111
  	if (raparm_hash[0].pb_head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2112
  		return 0;
54a66e548   Jeff Layton   knfsd: allocate r...
2113
2114
2115
2116
  	nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE);
  	if (nperbucket < 2)
  		nperbucket = 2;
  	cache_size = nperbucket * RAPARM_HASH_SIZE;
4b3bb06be   Yan Burman   [PATCH] nfsd: rep...
2117
2118
2119
  
  	dprintk("nfsd: allocating %d readahead buffers.
  ", cache_size);
54a66e548   Jeff Layton   knfsd: allocate r...
2120
2121
  
  	for (i = 0; i < RAPARM_HASH_SIZE; i++) {
4b3bb06be   Yan Burman   [PATCH] nfsd: rep...
2122
  		spin_lock_init(&raparm_hash[i].pb_lock);
54a66e548   Jeff Layton   knfsd: allocate r...
2123
2124
2125
2126
2127
2128
2129
2130
2131
  
  		raparm = &raparm_hash[i].pb_head;
  		for (j = 0; j < nperbucket; j++) {
  			*raparm = kzalloc(sizeof(struct raparms), GFP_KERNEL);
  			if (!*raparm)
  				goto out_nomem;
  			raparm = &(*raparm)->p_next;
  		}
  		*raparm = NULL;
4b3bb06be   Yan Burman   [PATCH] nfsd: rep...
2132
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2133
2134
  	nfsdstats.ra_size = cache_size;
  	return 0;
54a66e548   Jeff Layton   knfsd: allocate r...
2135
2136
2137
2138
2139
2140
  
  out_nomem:
  	dprintk("nfsd: kmalloc failed, freeing readahead buffers
  ");
  	nfsd_racache_shutdown();
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2141
  }
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
  
  #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
  struct posix_acl *
  nfsd_get_posix_acl(struct svc_fh *fhp, int type)
  {
  	struct inode *inode = fhp->fh_dentry->d_inode;
  	char *name;
  	void *value = NULL;
  	ssize_t size;
  	struct posix_acl *acl;
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
  	if (!IS_POSIXACL(inode))
  		return ERR_PTR(-EOPNOTSUPP);
  
  	switch (type) {
  	case ACL_TYPE_ACCESS:
  		name = POSIX_ACL_XATTR_ACCESS;
  		break;
  	case ACL_TYPE_DEFAULT:
  		name = POSIX_ACL_XATTR_DEFAULT;
  		break;
  	default:
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2163
  		return ERR_PTR(-EOPNOTSUPP);
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2164
  	}
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
2165
2166
2167
  	size = nfsd_getxattr(fhp->fh_dentry, name, &value);
  	if (size < 0)
  		return ERR_PTR(size);
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2168

a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2169
  	acl = posix_acl_from_xattr(value, size);
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
  	kfree(value);
  	return acl;
  }
  
  int
  nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
  {
  	struct inode *inode = fhp->fh_dentry->d_inode;
  	char *name;
  	void *value = NULL;
  	size_t size;
  	int error;
acfa4380e   Al Viro   inode->i_op is ne...
2182
  	if (!IS_POSIXACL(inode) ||
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2183
2184
2185
2186
  	    !inode->i_op->setxattr || !inode->i_op->removexattr)
  		return -EOPNOTSUPP;
  	switch(type) {
  		case ACL_TYPE_ACCESS:
334a13ec3   Christoph Hellwig   [PATCH] really re...
2187
  			name = POSIX_ACL_XATTR_ACCESS;
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2188
2189
  			break;
  		case ACL_TYPE_DEFAULT:
334a13ec3   Christoph Hellwig   [PATCH] really re...
2190
  			name = POSIX_ACL_XATTR_DEFAULT;
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2191
2192
2193
2194
2195
2196
  			break;
  		default:
  			return -EOPNOTSUPP;
  	}
  
  	if (acl && acl->a_count) {
334a13ec3   Christoph Hellwig   [PATCH] really re...
2197
  		size = posix_acl_xattr_size(acl->a_count);
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2198
2199
2200
  		value = kmalloc(size, GFP_KERNEL);
  		if (!value)
  			return -ENOMEM;
9ccfc29c6   Florin Malita   [PATCH] nfsd: sig...
2201
2202
  		error = posix_acl_to_xattr(acl, value, size);
  		if (error < 0)
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2203
  			goto getout;
9ccfc29c6   Florin Malita   [PATCH] nfsd: sig...
2204
  		size = error;
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2205
2206
  	} else
  		size = 0;
bad0dcffc   Al Viro   new helpers: fh_{...
2207
  	error = fh_want_write(fhp);
18f335aff   Dave Hansen   [PATCH] r/o bind ...
2208
2209
  	if (error)
  		goto getout;
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2210
  	if (size)
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
2211
  		error = vfs_setxattr(fhp->fh_dentry, name, value, size, 0);
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2212
2213
2214
2215
  	else {
  		if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
  			error = 0;
  		else {
5be196e5f   Christoph Hellwig   [PATCH] add vfs_*...
2216
  			error = vfs_removexattr(fhp->fh_dentry, name);
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2217
2218
2219
2220
  			if (error == -ENODATA)
  				error = 0;
  		}
  	}
bad0dcffc   Al Viro   new helpers: fh_{...
2221
  	fh_drop_write(fhp);
a257cdd0e   Andreas Gruenbacher   [PATCH] NFSD: Add...
2222
2223
2224
2225
2226
2227
  
  getout:
  	kfree(value);
  	return error;
  }
  #endif  /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */