Blame view

fs/nfs/dir.c 59.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  /*
   *  linux/fs/nfs/dir.c
   *
   *  Copyright (C) 1992  Rick Sladkey
   *
   *  nfs directory handling functions
   *
   * 10 Apr 1996	Added silly rename for unlink	--okir
   * 28 Sep 1996	Improved directory cache --okir
   * 23 Aug 1997  Claus Heine claus@momo.math.rwth-aachen.de 
   *              Re-implemented silly rename for unlink, newly implemented
   *              silly rename for nfs_rename() following the suggestions
   *              of Olaf Kirch (okir) found in this file.
   *              Following Linus comments on my original hack, this version
   *              depends only on the dcache stuff and doesn't touch the inode
   *              layer (iput() and friends).
   *  6 Jun 1999	Cache readdir lookups in the page cache. -DaveM
   */
  
  #include <linux/time.h>
  #include <linux/errno.h>
  #include <linux/stat.h>
  #include <linux/fcntl.h>
  #include <linux/string.h>
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/mm.h>
  #include <linux/sunrpc/clnt.h>
  #include <linux/nfs_fs.h>
  #include <linux/nfs_mount.h>
  #include <linux/pagemap.h>
873101b33   Chuck Lever   NFS: copy symlink...
32
  #include <linux/pagevec.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
  #include <linux/namei.h>
54ceac451   David Howells   NFS: Share NFS su...
34
  #include <linux/mount.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
35
  #include <linux/sched.h>
04e4bd1c6   Catalin Marinas   nfs: Ignore kmeml...
36
  #include <linux/kmemleak.h>
64c2ce8b7   Aneesh Kumar K.V   nfsv4: Switch to ...
37
  #include <linux/xattr.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
  
  #include "delegation.h"
91d5b4702   Chuck Lever   NFS: add I/O perf...
40
  #include "iostat.h"
4c30d56ed   Adrian Bunk   NFS: fs/nfs/dir.c...
41
  #include "internal.h"
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
42
  #include "fscache.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
  /* #define NFS_DEBUG_VERBOSE 1 */
  
  static int nfs_opendir(struct inode *, struct file *);
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
47
  static int nfs_closedir(struct inode *, struct file *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  static int nfs_readdir(struct file *, void *, filldir_t);
  static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *);
4acdaf27e   Al Viro   switch ->create()...
50
  static int nfs_create(struct inode *, struct dentry *, umode_t, struct nameidata *);
18bb1db3e   Al Viro   switch vfs_mkdir(...
51
  static int nfs_mkdir(struct inode *, struct dentry *, umode_t);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
  static int nfs_rmdir(struct inode *, struct dentry *);
  static int nfs_unlink(struct inode *, struct dentry *);
  static int nfs_symlink(struct inode *, struct dentry *, const char *);
  static int nfs_link(struct dentry *, struct inode *, struct dentry *);
1a67aafb5   Al Viro   switch ->mknod() ...
56
  static int nfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
  static int nfs_rename(struct inode *, struct dentry *,
  		      struct inode *, struct dentry *);
02c24a821   Josef Bacik   fs: push i_mutex ...
59
  static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
60
  static loff_t nfs_llseek_dir(struct file *, loff_t, int);
11de3b11e   Trond Myklebust   NFS: Fix a memory...
61
  static void nfs_readdir_clear_array(struct page*);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62

4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
63
  const struct file_operations nfs_dir_operations = {
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
64
  	.llseek		= nfs_llseek_dir,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
  	.read		= generic_read_dir,
  	.readdir	= nfs_readdir,
  	.open		= nfs_opendir,
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
68
  	.release	= nfs_closedir,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
  	.fsync		= nfs_fsync_dir,
  };
92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
71
  const struct inode_operations nfs_dir_inode_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
77
78
79
80
81
82
83
84
  	.create		= nfs_create,
  	.lookup		= nfs_lookup,
  	.link		= nfs_link,
  	.unlink		= nfs_unlink,
  	.symlink	= nfs_symlink,
  	.mkdir		= nfs_mkdir,
  	.rmdir		= nfs_rmdir,
  	.mknod		= nfs_mknod,
  	.rename		= nfs_rename,
  	.permission	= nfs_permission,
  	.getattr	= nfs_getattr,
  	.setattr	= nfs_setattr,
  };
11de3b11e   Trond Myklebust   NFS: Fix a memory...
85
86
  const struct address_space_operations nfs_dir_aops = {
  	.freepage = nfs_readdir_clear_array,
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
87
  };
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
88
  #ifdef CONFIG_NFS_V3
92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
89
  const struct inode_operations nfs3_dir_inode_operations = {
b7fa0554c   Andreas Gruenbacher   [PATCH] NFS: Add ...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  	.create		= nfs_create,
  	.lookup		= nfs_lookup,
  	.link		= nfs_link,
  	.unlink		= nfs_unlink,
  	.symlink	= nfs_symlink,
  	.mkdir		= nfs_mkdir,
  	.rmdir		= nfs_rmdir,
  	.mknod		= nfs_mknod,
  	.rename		= nfs_rename,
  	.permission	= nfs_permission,
  	.getattr	= nfs_getattr,
  	.setattr	= nfs_setattr,
  	.listxattr	= nfs3_listxattr,
  	.getxattr	= nfs3_getxattr,
  	.setxattr	= nfs3_setxattr,
  	.removexattr	= nfs3_removexattr,
  };
  #endif  /* CONFIG_NFS_V3 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
  #ifdef CONFIG_NFS_V4
  
  static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *);
4acdaf27e   Al Viro   switch ->create()...
111
  static int nfs_open_create(struct inode *dir, struct dentry *dentry, umode_t mode, struct nameidata *nd);
92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
112
  const struct inode_operations nfs4_dir_inode_operations = {
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
113
  	.create		= nfs_open_create,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
118
119
120
121
122
123
124
  	.lookup		= nfs_atomic_lookup,
  	.link		= nfs_link,
  	.unlink		= nfs_unlink,
  	.symlink	= nfs_symlink,
  	.mkdir		= nfs_mkdir,
  	.rmdir		= nfs_rmdir,
  	.mknod		= nfs_mknod,
  	.rename		= nfs_rename,
  	.permission	= nfs_permission,
  	.getattr	= nfs_getattr,
  	.setattr	= nfs_setattr,
64c2ce8b7   Aneesh Kumar K.V   nfsv4: Switch to ...
125
126
127
128
  	.getxattr	= generic_getxattr,
  	.setxattr	= generic_setxattr,
  	.listxattr	= generic_listxattr,
  	.removexattr	= generic_removexattr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
  };
  
  #endif /* CONFIG_NFS_V4 */
0c0308066   Trond Myklebust   NFS: Fix spurious...
132
  static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
133
134
135
136
  {
  	struct nfs_open_dir_context *ctx;
  	ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
  	if (ctx != NULL) {
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
137
  		ctx->duped = 0;
0c0308066   Trond Myklebust   NFS: Fix spurious...
138
  		ctx->attr_gencount = NFS_I(dir)->attr_gencount;
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
139
  		ctx->dir_cookie = 0;
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
140
  		ctx->dup_cookie = 0;
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
141
  		ctx->cred = get_rpccred(cred);
0c0308066   Trond Myklebust   NFS: Fix spurious...
142
143
144
  		return ctx;
  	}
  	return  ERR_PTR(-ENOMEM);
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
145
146
147
148
149
150
151
  }
  
  static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx)
  {
  	put_rpccred(ctx->cred);
  	kfree(ctx);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
155
156
157
  /*
   * Open file
   */
  static int
  nfs_opendir(struct inode *inode, struct file *filp)
  {
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
158
159
160
  	int res = 0;
  	struct nfs_open_dir_context *ctx;
  	struct rpc_cred *cred;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161

6da24bc9c   Chuck Lever   NFS: Use NFSDBG_F...
162
163
  	dfprintk(FILE, "NFS: open dir(%s/%s)
  ",
cc0dd2d10   Chuck Lever   NFS: Make nfs_ope...
164
165
166
167
  			filp->f_path.dentry->d_parent->d_name.name,
  			filp->f_path.dentry->d_name.name);
  
  	nfs_inc_stats(inode, NFSIOS_VFSOPEN);
1e7cb3dc1   Chuck Lever   NFS: directory tr...
168

480c2006e   Bryan Schumaker   NFS: Create nfs_o...
169
170
171
  	cred = rpc_lookup_cred();
  	if (IS_ERR(cred))
  		return PTR_ERR(cred);
0c0308066   Trond Myklebust   NFS: Fix spurious...
172
  	ctx = alloc_nfs_open_dir_context(inode, cred);
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
173
174
175
176
177
  	if (IS_ERR(ctx)) {
  		res = PTR_ERR(ctx);
  		goto out;
  	}
  	filp->private_data = ctx;
f5a73672d   Neil Brown   NFS: allow close-...
178
179
180
181
182
183
184
  	if (filp->f_path.dentry == filp->f_path.mnt->mnt_root) {
  		/* This is a mountpoint, so d_revalidate will never
  		 * have been called, so we need to refresh the
  		 * inode (for close-open consistency) ourselves.
  		 */
  		__nfs_revalidate_inode(NFS_SERVER(inode), inode);
  	}
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
185
186
  out:
  	put_rpccred(cred);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
  	return res;
  }
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
189
190
191
192
193
194
  static int
  nfs_closedir(struct inode *inode, struct file *filp)
  {
  	put_nfs_open_dir_context(filp->private_data);
  	return 0;
  }
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
195
196
197
198
  struct nfs_cache_array_entry {
  	u64 cookie;
  	u64 ino;
  	struct qstr string;
0b26a0bf6   Trond Myklebust   NFS: Ensure we re...
199
  	unsigned char d_type;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
200
201
202
203
204
205
206
207
  };
  
  struct nfs_cache_array {
  	unsigned int size;
  	int eof_index;
  	u64 last_cookie;
  	struct nfs_cache_array_entry array[0];
  };
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
208
  typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
  typedef struct {
  	struct file	*file;
  	struct page	*page;
  	unsigned long	page_index;
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
213
  	u64		*dir_cookie;
0aded708d   Trond Myklebust   NFS: Ensure we us...
214
  	u64		last_cookie;
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
215
  	loff_t		current_index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  	decode_dirent_t	decode;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
217

1f4eab7e7   Neil Brown   NFS: Set meaningf...
218
  	unsigned long	timestamp;
4704f0e27   Trond Myklebust   NFS: Fix the reso...
219
  	unsigned long	gencount;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
220
221
222
  	unsigned int	cache_entry_index;
  	unsigned int	plus:1;
  	unsigned int	eof:1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
  } nfs_readdir_descriptor_t;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
224
225
  /*
   * The caller is responsible for calling nfs_readdir_release_array(page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
   */
  static
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
228
229
  struct nfs_cache_array *nfs_readdir_get_array(struct page *page)
  {
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
230
  	void *ptr;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
231
232
  	if (page == NULL)
  		return ERR_PTR(-EIO);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
233
234
235
236
  	ptr = kmap(page);
  	if (ptr == NULL)
  		return ERR_PTR(-ENOMEM);
  	return ptr;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
237
238
239
240
241
242
243
244
245
246
247
248
  }
  
  static
  void nfs_readdir_release_array(struct page *page)
  {
  	kunmap(page);
  }
  
  /*
   * we are freeing strings created by nfs_add_to_readdir_array()
   */
  static
11de3b11e   Trond Myklebust   NFS: Fix a memory...
249
  void nfs_readdir_clear_array(struct page *page)
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
250
  {
11de3b11e   Trond Myklebust   NFS: Fix a memory...
251
  	struct nfs_cache_array *array;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
252
  	int i;
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
253

11de3b11e   Trond Myklebust   NFS: Fix a memory...
254
  	array = kmap_atomic(page, KM_USER0);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
255
256
  	for (i = 0; i < array->size; i++)
  		kfree(array->array[i].string.name);
11de3b11e   Trond Myklebust   NFS: Fix a memory...
257
  	kunmap_atomic(array, KM_USER0);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
258
259
260
261
262
263
264
265
  }
  
  /*
   * the caller is responsible for freeing qstr.name
   * when called by nfs_readdir_add_to_array, the strings will be freed in
   * nfs_clear_readdir_array()
   */
  static
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
266
  int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len)
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
267
268
269
  {
  	string->len = len;
  	string->name = kmemdup(name, len, GFP_KERNEL);
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
270
271
  	if (string->name == NULL)
  		return -ENOMEM;
04e4bd1c6   Catalin Marinas   nfs: Ignore kmeml...
272
273
274
275
276
  	/*
  	 * Avoid a kmemleak false positive. The pointer to the name is stored
  	 * in a page cache page which kmemleak does not scan.
  	 */
  	kmemleak_not_leak(string->name);
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
277
278
  	string->hash = full_name_hash(name, len);
  	return 0;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
279
280
281
282
283
284
  }
  
  static
  int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
  {
  	struct nfs_cache_array *array = nfs_readdir_get_array(page);
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
285
286
  	struct nfs_cache_array_entry *cache_entry;
  	int ret;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
287
288
  	if (IS_ERR(array))
  		return PTR_ERR(array);
3020093f5   Trond Myklebust   NFS: Correct the ...
289
290
291
292
  
  	cache_entry = &array->array[array->size];
  
  	/* Check that this entry lies within the page bounds */
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
293
  	ret = -ENOSPC;
3020093f5   Trond Myklebust   NFS: Correct the ...
294
  	if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE)
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
295
  		goto out;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
296

4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
297
298
  	cache_entry->cookie = entry->prev_cookie;
  	cache_entry->ino = entry->ino;
0b26a0bf6   Trond Myklebust   NFS: Ensure we re...
299
  	cache_entry->d_type = entry->d_type;
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
300
301
302
  	ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len);
  	if (ret)
  		goto out;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
303
  	array->last_cookie = entry->cookie;
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
304
  	array->size++;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
305
  	if (entry->eof != 0)
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
306
  		array->eof_index = array->size;
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
307
  out:
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
308
  	nfs_readdir_release_array(page);
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
309
  	return ret;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
310
311
312
313
314
315
316
317
318
319
320
  }
  
  static
  int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc)
  {
  	loff_t diff = desc->file->f_pos - desc->current_index;
  	unsigned int index;
  
  	if (diff < 0)
  		goto out_eof;
  	if (diff >= array->size) {
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
321
  		if (array->eof_index >= 0)
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
322
  			goto out_eof;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
323
324
325
326
327
328
  		return -EAGAIN;
  	}
  
  	index = (unsigned int)diff;
  	*desc->dir_cookie = array->array[index].cookie;
  	desc->cache_entry_index = index;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
329
330
331
332
333
334
335
336
337
338
  	return 0;
  out_eof:
  	desc->eof = 1;
  	return -EBADCOOKIE;
  }
  
  static
  int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc)
  {
  	int i;
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
339
  	loff_t new_pos;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
340
341
342
  	int status = -EAGAIN;
  
  	for (i = 0; i < array->size; i++) {
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
343
  		if (array->array[i].cookie == *desc->dir_cookie) {
0c0308066   Trond Myklebust   NFS: Fix spurious...
344
345
  			struct nfs_inode *nfsi = NFS_I(desc->file->f_path.dentry->d_inode);
  			struct nfs_open_dir_context *ctx = desc->file->private_data;
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
346
  			new_pos = desc->current_index + i;
0c0308066   Trond Myklebust   NFS: Fix spurious...
347
348
349
350
351
352
353
354
355
356
  			if (ctx->attr_gencount != nfsi->attr_gencount
  			    || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) {
  				ctx->duped = 0;
  				ctx->attr_gencount = nfsi->attr_gencount;
  			} else if (new_pos < desc->file->f_pos) {
  				if (ctx->duped > 0
  				    && ctx->dup_cookie == *desc->dir_cookie) {
  					if (printk_ratelimit()) {
  						pr_notice("NFS: directory %s/%s contains a readdir loop."
  								"Please contact your server vendor.  "
374e4e3ec   Bryan Schumaker   Additional readdi...
357
358
  								"The file: %s has duplicate cookie %llu
  ",
0c0308066   Trond Myklebust   NFS: Fix spurious...
359
360
  								desc->file->f_dentry->d_parent->d_name.name,
  								desc->file->f_dentry->d_name.name,
374e4e3ec   Bryan Schumaker   Additional readdi...
361
  								array->array[i].string.name,
0c0308066   Trond Myklebust   NFS: Fix spurious...
362
363
364
365
366
  								*desc->dir_cookie);
  					}
  					status = -ELOOP;
  					goto out;
  				}
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
367
  				ctx->dup_cookie = *desc->dir_cookie;
0c0308066   Trond Myklebust   NFS: Fix spurious...
368
  				ctx->duped = -1;
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
369
370
  			}
  			desc->file->f_pos = new_pos;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
371
  			desc->cache_entry_index = i;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
372
  			return 0;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
373
374
  		}
  	}
47c716cbf   Trond Myklebust   NFS: Readdir clea...
375
  	if (array->eof_index >= 0) {
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
376
  		status = -EBADCOOKIE;
18fb5fe40   Trond Myklebust   NFS: nfs_readdir_...
377
378
  		if (*desc->dir_cookie == array->last_cookie)
  			desc->eof = 1;
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
379
  	}
0c0308066   Trond Myklebust   NFS: Fix spurious...
380
  out:
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
381
382
383
384
385
386
387
  	return status;
  }
  
  static
  int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc)
  {
  	struct nfs_cache_array *array;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
388
  	int status;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
389
390
391
392
393
394
395
396
397
398
399
  
  	array = nfs_readdir_get_array(desc->page);
  	if (IS_ERR(array)) {
  		status = PTR_ERR(array);
  		goto out;
  	}
  
  	if (*desc->dir_cookie == 0)
  		status = nfs_readdir_search_for_pos(array, desc);
  	else
  		status = nfs_readdir_search_for_cookie(array, desc);
47c716cbf   Trond Myklebust   NFS: Readdir clea...
400
  	if (status == -EAGAIN) {
0aded708d   Trond Myklebust   NFS: Ensure we us...
401
  		desc->last_cookie = array->last_cookie;
e47c085af   Trond Myklebust   NFS: Ensure that ...
402
  		desc->current_index += array->size;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
403
404
  		desc->page_index++;
  	}
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
405
406
407
408
409
410
411
  	nfs_readdir_release_array(desc->page);
  out:
  	return status;
  }
  
  /* Fill a page with xdr information before transferring to the cache page */
  static
56e4ebf87   Bryan Schumaker   NFS: readdir with...
412
  int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
413
  			struct nfs_entry *entry, struct file *file, struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
  {
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
415
416
  	struct nfs_open_dir_context *ctx = file->private_data;
  	struct rpc_cred	*cred = ctx->cred;
4704f0e27   Trond Myklebust   NFS: Fix the reso...
417
  	unsigned long	timestamp, gencount;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
  	int		error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
   again:
  	timestamp = jiffies;
4704f0e27   Trond Myklebust   NFS: Fix the reso...
421
  	gencount = nfs_inc_attr_generation_counter();
56e4ebf87   Bryan Schumaker   NFS: readdir with...
422
  	error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
425
426
427
  					  NFS_SERVER(inode)->dtsize, desc->plus);
  	if (error < 0) {
  		/* We requested READDIRPLUS, but the server doesn't grok it */
  		if (error == -ENOTSUPP && desc->plus) {
  			NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS;
3a10c30ac   Benny Halevy   nfs: obliterate N...
428
  			clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
433
  			desc->plus = 0;
  			goto again;
  		}
  		goto error;
  	}
1f4eab7e7   Neil Brown   NFS: Set meaningf...
434
  	desc->timestamp = timestamp;
4704f0e27   Trond Myklebust   NFS: Fix the reso...
435
  	desc->gencount = gencount;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
436
437
  error:
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
  }
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
439
440
  static int xdr_decode(nfs_readdir_descriptor_t *desc,
  		      struct nfs_entry *entry, struct xdr_stream *xdr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  {
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
442
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443

573c4e1ef   Chuck Lever   NFS: Simplify ->d...
444
445
446
  	error = desc->decode(xdr, entry, desc->plus);
  	if (error)
  		return error;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
447
448
449
  	entry->fattr->time_start = desc->timestamp;
  	entry->fattr->gencount = desc->gencount;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
  }
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
451
452
453
  static
  int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
  {
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
454
455
  	if (dentry->d_inode == NULL)
  		goto different;
37a09f074   Trond Myklebust   NFS: Fix a readdi...
456
  	if (nfs_compare_fh(entry->fh, NFS_FH(dentry->d_inode)) != 0)
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
457
458
459
460
461
462
463
464
465
  		goto different;
  	return 1;
  different:
  	return 0;
  }
  
  static
  void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
  {
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
466
467
468
469
470
471
  	struct qstr filename = {
  		.len = entry->len,
  		.name = entry->name,
  	};
  	struct dentry *dentry;
  	struct dentry *alias;
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
472
473
  	struct inode *dir = parent->d_inode;
  	struct inode *inode;
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
474
475
476
477
478
479
480
  	if (filename.name[0] == '.') {
  		if (filename.len == 1)
  			return;
  		if (filename.len == 2 && filename.name[1] == '.')
  			return;
  	}
  	filename.hash = full_name_hash(filename.name, filename.len);
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
481

4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
482
  	dentry = d_lookup(parent, &filename);
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
483
484
485
486
487
488
489
490
491
492
493
  	if (dentry != NULL) {
  		if (nfs_same_file(dentry, entry)) {
  			nfs_refresh_inode(dentry->d_inode, entry->fattr);
  			goto out;
  		} else {
  			d_drop(dentry);
  			dput(dentry);
  		}
  	}
  
  	dentry = d_alloc(parent, &filename);
4a201d6e3   Trond Myklebust   NFS: Ensure we ch...
494
495
  	if (dentry == NULL)
  		return;
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
  	inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
  	if (IS_ERR(inode))
  		goto out;
  
  	alias = d_materialise_unique(dentry, inode);
  	if (IS_ERR(alias))
  		goto out;
  	else if (alias) {
  		nfs_set_verifier(alias, nfs_save_change_attribute(dir));
  		dput(alias);
  	} else
  		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  
  out:
  	dput(dentry);
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
511
  }
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
512
513
  /* Perform conversion from xdr to cache array */
  static
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
514
  int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry,
6650239a4   Trond Myklebust   NFS: Don't use vm...
515
  				struct page **xdr_pages, struct page *page, unsigned int buflen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  {
babddc72a   Bryan Schumaker   NFS: decode_diren...
517
  	struct xdr_stream stream;
f7da7a129   Benny Halevy   SUNRPC: introduce...
518
  	struct xdr_buf buf;
6650239a4   Trond Myklebust   NFS: Don't use vm...
519
  	struct page *scratch;
994243808   Bryan Schumaker   NFS: check xdr_de...
520
  	struct nfs_cache_array *array;
5c346854d   Trond Myklebust   NFS: Assume eof i...
521
522
  	unsigned int count = 0;
  	int status;
babddc72a   Bryan Schumaker   NFS: decode_diren...
523

6650239a4   Trond Myklebust   NFS: Don't use vm...
524
525
526
  	scratch = alloc_page(GFP_KERNEL);
  	if (scratch == NULL)
  		return -ENOMEM;
babddc72a   Bryan Schumaker   NFS: decode_diren...
527

f7da7a129   Benny Halevy   SUNRPC: introduce...
528
  	xdr_init_decode_pages(&stream, &buf, xdr_pages, buflen);
6650239a4   Trond Myklebust   NFS: Don't use vm...
529
  	xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
994243808   Bryan Schumaker   NFS: check xdr_de...
530
531
532
  
  	do {
  		status = xdr_decode(desc, entry, &stream);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
533
534
535
  		if (status != 0) {
  			if (status == -EAGAIN)
  				status = 0;
994243808   Bryan Schumaker   NFS: check xdr_de...
536
  			break;
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
537
  		}
994243808   Bryan Schumaker   NFS: check xdr_de...
538

5c346854d   Trond Myklebust   NFS: Assume eof i...
539
  		count++;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
540
  		if (desc->plus != 0)
d39ab9de3   Bryan Schumaker   NFS: re-add readd...
541
  			nfs_prime_dcache(desc->file->f_path.dentry, entry);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
542
543
544
545
  
  		status = nfs_readdir_add_to_array(entry, page);
  		if (status != 0)
  			break;
994243808   Bryan Schumaker   NFS: check xdr_de...
546
  	} while (!entry->eof);
47c716cbf   Trond Myklebust   NFS: Readdir clea...
547
  	if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) {
994243808   Bryan Schumaker   NFS: check xdr_de...
548
  		array = nfs_readdir_get_array(page);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
549
550
551
552
  		if (!IS_ERR(array)) {
  			array->eof_index = array->size;
  			status = 0;
  			nfs_readdir_release_array(page);
5c346854d   Trond Myklebust   NFS: Assume eof i...
553
554
  		} else
  			status = PTR_ERR(array);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  	}
6650239a4   Trond Myklebust   NFS: Don't use vm...
556
557
  
  	put_page(scratch);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
558
  	return status;
56e4ebf87   Bryan Schumaker   NFS: readdir with...
559
560
561
562
563
564
565
566
567
568
569
570
571
572
  }
  
  static
  void nfs_readdir_free_pagearray(struct page **pages, unsigned int npages)
  {
  	unsigned int i;
  	for (i = 0; i < npages; i++)
  		put_page(pages[i]);
  }
  
  static
  void nfs_readdir_free_large_page(void *ptr, struct page **pages,
  		unsigned int npages)
  {
56e4ebf87   Bryan Schumaker   NFS: readdir with...
573
574
575
576
577
578
579
580
  	nfs_readdir_free_pagearray(pages, npages);
  }
  
  /*
   * nfs_readdir_large_page will allocate pages that must be freed with a call
   * to nfs_readdir_free_large_page
   */
  static
6650239a4   Trond Myklebust   NFS: Don't use vm...
581
  int nfs_readdir_large_page(struct page **pages, unsigned int npages)
56e4ebf87   Bryan Schumaker   NFS: readdir with...
582
  {
56e4ebf87   Bryan Schumaker   NFS: readdir with...
583
584
585
586
587
588
589
590
  	unsigned int i;
  
  	for (i = 0; i < npages; i++) {
  		struct page *page = alloc_page(GFP_KERNEL);
  		if (page == NULL)
  			goto out_freepages;
  		pages[i] = page;
  	}
6650239a4   Trond Myklebust   NFS: Don't use vm...
591
  	return 0;
56e4ebf87   Bryan Schumaker   NFS: readdir with...
592

56e4ebf87   Bryan Schumaker   NFS: readdir with...
593
594
  out_freepages:
  	nfs_readdir_free_pagearray(pages, i);
6650239a4   Trond Myklebust   NFS: Don't use vm...
595
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
  }
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
597
598
  static
  int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode)
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
599
  {
56e4ebf87   Bryan Schumaker   NFS: readdir with...
600
601
  	struct page *pages[NFS_MAX_READDIR_PAGES];
  	void *pages_ptr = NULL;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
602
603
604
  	struct nfs_entry entry;
  	struct file	*file = desc->file;
  	struct nfs_cache_array *array;
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
605
  	int status = -ENOMEM;
56e4ebf87   Bryan Schumaker   NFS: readdir with...
606
  	unsigned int array_size = ARRAY_SIZE(pages);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
607
608
  
  	entry.prev_cookie = 0;
0aded708d   Trond Myklebust   NFS: Ensure we us...
609
  	entry.cookie = desc->last_cookie;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
610
611
612
  	entry.eof = 0;
  	entry.fh = nfs_alloc_fhandle();
  	entry.fattr = nfs_alloc_fattr();
573c4e1ef   Chuck Lever   NFS: Simplify ->d...
613
  	entry.server = NFS_SERVER(inode);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
614
615
  	if (entry.fh == NULL || entry.fattr == NULL)
  		goto out;
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
616

d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
617
  	array = nfs_readdir_get_array(page);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
618
619
620
621
  	if (IS_ERR(array)) {
  		status = PTR_ERR(array);
  		goto out;
  	}
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
622
623
  	memset(array, 0, sizeof(struct nfs_cache_array));
  	array->eof_index = -1;
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
624

6650239a4   Trond Myklebust   NFS: Don't use vm...
625
626
  	status = nfs_readdir_large_page(pages, array_size);
  	if (status < 0)
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
627
628
  		goto out_release_array;
  	do {
ac3961282   Trond Myklebust   NFS: readdir shou...
629
  		unsigned int pglen;
56e4ebf87   Bryan Schumaker   NFS: readdir with...
630
  		status = nfs_readdir_xdr_filler(pages, desc, &entry, file, inode);
babddc72a   Bryan Schumaker   NFS: decode_diren...
631

d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
632
  		if (status < 0)
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
633
  			break;
ac3961282   Trond Myklebust   NFS: readdir shou...
634
  		pglen = status;
6650239a4   Trond Myklebust   NFS: Don't use vm...
635
  		status = nfs_readdir_page_filler(desc, &entry, pages, page, pglen);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
636
637
638
639
640
641
  		if (status < 0) {
  			if (status == -ENOSPC)
  				status = 0;
  			break;
  		}
  	} while (array->eof_index < 0);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
642

56e4ebf87   Bryan Schumaker   NFS: readdir with...
643
  	nfs_readdir_free_large_page(pages_ptr, pages, array_size);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
644
645
646
647
648
  out_release_array:
  	nfs_readdir_release_array(page);
  out:
  	nfs_free_fattr(entry.fattr);
  	nfs_free_fhandle(entry.fh);
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
649
650
651
652
  	return status;
  }
  
  /*
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
653
654
655
656
   * Now we cache directories properly, by converting xdr information
   * to an array that can be used for lookups later.  This results in
   * fewer cache pages, since we can store more information on each page.
   * We only need to convert from xdr once so future lookups are much simpler
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
   */
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
658
659
  static
  int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
  {
01cce933d   Josef "Jeff" Sipek   [PATCH] nfs: chan...
661
  	struct inode	*inode = desc->file->f_path.dentry->d_inode;
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
662
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663

8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
664
665
  	ret = nfs_readdir_xdr_to_array(desc, page, inode);
  	if (ret < 0)
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
666
667
  		goto error;
  	SetPageUptodate(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668

d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
669
670
671
  	if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) {
  		/* Should never happen */
  		nfs_zap_mapping(inode, inode->i_mapping);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
  	}
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
673
674
675
676
  	unlock_page(page);
  	return 0;
   error:
  	unlock_page(page);
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
677
  	return ret;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
678
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679

d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
680
681
682
  static
  void cache_page_release(nfs_readdir_descriptor_t *desc)
  {
11de3b11e   Trond Myklebust   NFS: Fix a memory...
683
684
  	if (!desc->page->mapping)
  		nfs_readdir_clear_array(desc->page);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
685
686
687
688
689
690
691
  	page_cache_release(desc->page);
  	desc->page = NULL;
  }
  
  static
  struct page *get_cache_page(nfs_readdir_descriptor_t *desc)
  {
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
692
  	return read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping,
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
693
  			desc->page_index, (filler_t *)nfs_readdir_filler, desc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
696
  }
  
  /*
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
697
   * Returns 0 if desc->dir_cookie was found on page desc->page_index
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
   */
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
699
700
701
702
703
704
705
706
707
708
  static
  int find_cache_page(nfs_readdir_descriptor_t *desc)
  {
  	int res;
  
  	desc->page = get_cache_page(desc);
  	if (IS_ERR(desc->page))
  		return PTR_ERR(desc->page);
  
  	res = nfs_readdir_search_array(desc);
47c716cbf   Trond Myklebust   NFS: Readdir clea...
709
710
  	if (res != 0)
  		cache_page_release(desc);
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
711
712
713
714
  	return res;
  }
  
  /* Search for desc->dir_cookie from the beginning of the page cache */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
  static inline
  int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
  {
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
718
  	int res;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
719

0aded708d   Trond Myklebust   NFS: Ensure we us...
720
  	if (desc->page_index == 0) {
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
721
  		desc->current_index = 0;
0aded708d   Trond Myklebust   NFS: Ensure we us...
722
723
  		desc->last_cookie = 0;
  	}
47c716cbf   Trond Myklebust   NFS: Readdir clea...
724
  	do {
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
725
  		res = find_cache_page(desc);
47c716cbf   Trond Myklebust   NFS: Readdir clea...
726
  	} while (res == -EAGAIN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
730
731
732
733
734
735
736
  /*
   * Once we've found the start of the dirent within a page: fill 'er up...
   */
  static 
  int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
  		   filldir_t filldir)
  {
  	struct file	*file = desc->file;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
737
738
739
  	int i = 0;
  	int res = 0;
  	struct nfs_cache_array *array = NULL;
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
740
  	struct nfs_open_dir_context *ctx = file->private_data;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
741
  	array = nfs_readdir_get_array(desc->page);
e7c58e974   Trond Myklebust   NFS: Fix a page l...
742
743
744
745
  	if (IS_ERR(array)) {
  		res = PTR_ERR(array);
  		goto out;
  	}
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
746
747
  
  	for (i = desc->cache_entry_index; i < array->size; i++) {
ece0b4233   Trond Myklebust   NFS: Don't ignore...
748
  		struct nfs_cache_array_entry *ent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749

ece0b4233   Trond Myklebust   NFS: Don't ignore...
750
751
  		ent = &array->array[i];
  		if (filldir(dirent, ent->string.name, ent->string.len,
0b26a0bf6   Trond Myklebust   NFS: Ensure we re...
752
753
  		    file->f_pos, nfs_compat_user_ino64(ent->ino),
  		    ent->d_type) < 0) {
ece0b4233   Trond Myklebust   NFS: Don't ignore...
754
  			desc->eof = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
  			break;
ece0b4233   Trond Myklebust   NFS: Don't ignore...
756
  		}
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
757
  		file->f_pos++;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
758
759
760
761
  		if (i < (array->size-1))
  			*desc->dir_cookie = array->array[i+1].cookie;
  		else
  			*desc->dir_cookie = array->last_cookie;
0c0308066   Trond Myklebust   NFS: Fix spurious...
762
763
  		if (ctx->duped != 0)
  			ctx->duped = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
  	}
47c716cbf   Trond Myklebust   NFS: Readdir clea...
765
  	if (array->eof_index >= 0)
8cd51a0cc   Trond Myklebust   NFS: Fix a couple...
766
  		desc->eof = 1;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
767
768
  
  	nfs_readdir_release_array(desc->page);
e7c58e974   Trond Myklebust   NFS: Fix a page l...
769
  out:
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
770
  	cache_page_release(desc);
1e7cb3dc1   Chuck Lever   NFS: directory tr...
771
772
773
  	dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d
  ",
  			(unsigned long long)*desc->dir_cookie, res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
  	return res;
  }
  
  /*
   * If we cannot find a cookie in our cache, we suspect that this is
   * because it points to a deleted file, so we ask the server to return
   * whatever it thinks is the next entry. We then feed this to filldir.
   * If all goes well, we should then be able to find our way round the
   * cache on the next call to readdir_search_pagecache();
   *
   * NOTE: we cannot add the anonymous page to the pagecache because
   *	 the data it contains might not be page aligned. Besides,
   *	 we should already have a complete representation of the
   *	 directory in the page cache by the time we get here.
   */
  static inline
  int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
  		     filldir_t filldir)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
794
  	struct page	*page = NULL;
  	int		status;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
795
  	struct inode *inode = desc->file->f_path.dentry->d_inode;
0c0308066   Trond Myklebust   NFS: Fix spurious...
796
  	struct nfs_open_dir_context *ctx = desc->file->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797

1e7cb3dc1   Chuck Lever   NFS: directory tr...
798
799
800
  	dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu
  ",
  			(unsigned long long)*desc->dir_cookie);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
802
803
804
805
806
  
  	page = alloc_page(GFP_HIGHUSER);
  	if (!page) {
  		status = -ENOMEM;
  		goto out;
  	}
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
807

7a8e1dc34   Trond Myklebust   NFS: Fix a page l...
808
  	desc->page_index = 0;
0aded708d   Trond Myklebust   NFS: Ensure we us...
809
  	desc->last_cookie = *desc->dir_cookie;
7a8e1dc34   Trond Myklebust   NFS: Fix a page l...
810
  	desc->page = page;
0c0308066   Trond Myklebust   NFS: Fix spurious...
811
  	ctx->duped = 0;
7a8e1dc34   Trond Myklebust   NFS: Fix a page l...
812

85f8607e1   Trond Myklebust   NFS: Fix the erro...
813
814
  	status = nfs_readdir_xdr_to_array(desc, page, inode);
  	if (status < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815
816
817
  		goto out_release;
  
  	status = nfs_do_filldir(desc, dirent, filldir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
   out:
1e7cb3dc1   Chuck Lever   NFS: directory tr...
819
820
  	dfprintk(DIRCACHE, "NFS: %s: returns %d
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
821
  			__func__, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
  	return status;
   out_release:
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
824
  	cache_page_release(desc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
826
  	goto out;
  }
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
827
828
829
  /* The file offset position represents the dirent entry number.  A
     last cookie cache takes care of the common case of reading the
     whole directory.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
831
832
   */
  static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
  {
01cce933d   Josef "Jeff" Sipek   [PATCH] nfs: chan...
833
  	struct dentry	*dentry = filp->f_path.dentry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
836
  	struct inode	*inode = dentry->d_inode;
  	nfs_readdir_descriptor_t my_desc,
  			*desc = &my_desc;
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
837
  	struct nfs_open_dir_context *dir_ctx = filp->private_data;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
838
  	int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839

6da24bc9c   Chuck Lever   NFS: Use NFSDBG_F...
840
841
  	dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu
  ",
1e7cb3dc1   Chuck Lever   NFS: directory tr...
842
843
  			dentry->d_parent->d_name.name, dentry->d_name.name,
  			(long long)filp->f_pos);
91d5b4702   Chuck Lever   NFS: add I/O perf...
844
  	nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
  	/*
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
846
  	 * filp->f_pos points to the dirent entry number.
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
847
  	 * *desc->dir_cookie has the cookie for the next entry. We have
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
848
849
  	 * to either find the entry with the appropriate number or
  	 * revalidate the cookie.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
851
852
853
  	 */
  	memset(desc, 0, sizeof(*desc));
  
  	desc->file = filp;
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
854
  	desc->dir_cookie = &dir_ctx->dir_cookie;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
856
  	desc->decode = NFS_PROTO(inode)->decode_dirent;
  	desc->plus = NFS_USE_READDIRPLUS(inode);
565277f63   Trond Myklebust   NFS: Fix a race i...
857
  	nfs_block_sillyrename(dentry);
1cda707d5   Trond Myklebust   NFS: Remove requi...
858
  	res = nfs_revalidate_mapping(inode, filp->f_mapping);
fccca7fc6   Trond Myklebust   NFS: Fix a sillyr...
859
860
  	if (res < 0)
  		goto out;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
861
  	do {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
  		res = readdir_search_pagecache(desc);
00a926422   Olivier Galibert   [PATCH] NFS: Hide...
863

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
  		if (res == -EBADCOOKIE) {
ece0b4233   Trond Myklebust   NFS: Don't ignore...
865
  			res = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
  			/* This means either end of directory */
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
867
  			if (*desc->dir_cookie && desc->eof == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
  				/* Or that the server has 'lost' a cookie */
  				res = uncached_readdir(desc, dirent, filldir);
ece0b4233   Trond Myklebust   NFS: Don't ignore...
870
  				if (res == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
872
  					continue;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
  			break;
  		}
  		if (res == -ETOOSMALL && desc->plus) {
3a10c30ac   Benny Halevy   nfs: obliterate N...
876
  			clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
  			nfs_zap_caches(inode);
baf57a09e   Trond Myklebust   NFS: Optimise the...
878
  			desc->page_index = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
  			desc->plus = 0;
d1bacf9eb   Bryan Schumaker   NFS: add readdir ...
880
  			desc->eof = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
882
883
884
885
886
  			continue;
  		}
  		if (res < 0)
  			break;
  
  		res = nfs_do_filldir(desc, dirent, filldir);
ece0b4233   Trond Myklebust   NFS: Don't ignore...
887
  		if (res < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
  			break;
47c716cbf   Trond Myklebust   NFS: Readdir clea...
889
  	} while (!desc->eof);
fccca7fc6   Trond Myklebust   NFS: Fix a sillyr...
890
  out:
565277f63   Trond Myklebust   NFS: Fix a race i...
891
  	nfs_unblock_sillyrename(dentry);
1e7cb3dc1   Chuck Lever   NFS: directory tr...
892
893
  	if (res > 0)
  		res = 0;
aa49b4cf7   Trond Myklebust   NFS: Reduce stack...
894
895
  	dfprintk(FILE, "NFS: readdir(%s/%s) returns %d
  ",
1e7cb3dc1   Chuck Lever   NFS: directory tr...
896
897
898
  			dentry->d_parent->d_name.name, dentry->d_name.name,
  			res);
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
  }
10afec908   Trond Myklebust   NFS: Fix some 'sp...
900
  static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
901
  {
b84e06c58   Chuck Lever   NFS: Make nfs_lls...
902
903
  	struct dentry *dentry = filp->f_path.dentry;
  	struct inode *inode = dentry->d_inode;
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
904
  	struct nfs_open_dir_context *dir_ctx = filp->private_data;
b84e06c58   Chuck Lever   NFS: Make nfs_lls...
905

6da24bc9c   Chuck Lever   NFS: Use NFSDBG_F...
906
907
  	dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)
  ",
b84e06c58   Chuck Lever   NFS: Make nfs_lls...
908
909
910
911
912
  			dentry->d_parent->d_name.name,
  			dentry->d_name.name,
  			offset, origin);
  
  	mutex_lock(&inode->i_mutex);
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
913
914
915
916
917
918
919
920
921
922
923
924
  	switch (origin) {
  		case 1:
  			offset += filp->f_pos;
  		case 0:
  			if (offset >= 0)
  				break;
  		default:
  			offset = -EINVAL;
  			goto out;
  	}
  	if (offset != filp->f_pos) {
  		filp->f_pos = offset;
480c2006e   Bryan Schumaker   NFS: Create nfs_o...
925
  		dir_ctx->dir_cookie = 0;
8ef2ce3e1   Bryan Schumaker   NFS: Detect loops...
926
  		dir_ctx->duped = 0;
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
927
928
  	}
  out:
b84e06c58   Chuck Lever   NFS: Make nfs_lls...
929
  	mutex_unlock(&inode->i_mutex);
f0dd2136d   Trond Myklebust   [PATCH] NFS: Clea...
930
931
  	return offset;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
933
934
935
  /*
   * All directory operations under NFS are synchronous, so fsync()
   * is a dummy operation.
   */
02c24a821   Josef Bacik   fs: push i_mutex ...
936
937
  static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
  			 int datasync)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
  {
7ea808591   Christoph Hellwig   drop unused dentr...
939
  	struct dentry *dentry = filp->f_path.dentry;
02c24a821   Josef Bacik   fs: push i_mutex ...
940
  	struct inode *inode = dentry->d_inode;
7ea808591   Christoph Hellwig   drop unused dentr...
941

6da24bc9c   Chuck Lever   NFS: Use NFSDBG_F...
942
943
  	dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d
  ",
1e7cb3dc1   Chuck Lever   NFS: directory tr...
944
945
  			dentry->d_parent->d_name.name, dentry->d_name.name,
  			datasync);
02c24a821   Josef Bacik   fs: push i_mutex ...
946
  	mutex_lock(&inode->i_mutex);
549177863   Chuck Lever   NFS: Make nfs_fsy...
947
  	nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC);
02c24a821   Josef Bacik   fs: push i_mutex ...
948
  	mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
950
  	return 0;
  }
bfc69a456   Trond Myklebust   NFS: define a fun...
951
952
953
954
955
956
957
958
959
960
961
962
  /**
   * nfs_force_lookup_revalidate - Mark the directory as having changed
   * @dir - pointer to directory inode
   *
   * This forces the revalidation code in nfs_lookup_revalidate() to do a
   * full lookup on all child dentries of 'dir' whenever a change occurs
   * on the server that might have invalidated our dcache.
   *
   * The caller should be holding dir->i_lock
   */
  void nfs_force_lookup_revalidate(struct inode *dir)
  {
011935a0a   Trond Myklebust   NFS: Fix a resolu...
963
  	NFS_I(dir)->cache_change_attribute++;
bfc69a456   Trond Myklebust   NFS: define a fun...
964
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
966
967
968
969
  /*
   * A check for whether or not the parent directory has changed.
   * In the case it has, we assume that the dentries are untrustworthy
   * and may need to be looked up again.
   */
c79ba787c   Trond Myklebust   NFS: Dont clobber...
970
  static int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
972
973
  {
  	if (IS_ROOT(dentry))
  		return 1;
4eec952e4   Trond Myklebust   NFS: Add options ...
974
975
  	if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE)
  		return 0;
f2c77f4e6   Trond Myklebust   NFS: Optimise nfs...
976
977
978
979
980
981
982
983
  	if (!nfs_verify_change_attribute(dir, dentry->d_time))
  		return 0;
  	/* Revalidate nfsi->cache_change_attribute before we declare a match */
  	if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
  		return 0;
  	if (!nfs_verify_change_attribute(dir, dentry->d_time))
  		return 0;
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
  /*
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
986
987
988
   * Return the intent data that applies to this particular path component
   *
   * Note that the current set of intents only apply to the very last
8aeb376ca   Al Viro   nfs: LOOKUP_{OPEN...
989
990
   * component of the path and none of them is set before that last
   * component.
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
991
   */
34286d666   Nick Piggin   fs: rcu-walk awar...
992
993
  static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd,
  						unsigned int mask)
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
994
  {
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
995
996
997
998
  	return nd->flags & mask;
  }
  
  /*
a12802cab   Trond Myklebust   NFS: Be strict ab...
999
1000
1001
1002
1003
1004
1005
   * Use intent information to check whether or not we're going to do
   * an O_EXCL create using this path component.
   */
  static int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd)
  {
  	if (NFS_PROTO(dir)->version == 2)
  		return 0;
3516586a4   Al Viro   [PATCH] make O_EX...
1006
  	return nd && nfs_lookup_check_intent(nd, LOOKUP_EXCL);
a12802cab   Trond Myklebust   NFS: Be strict ab...
1007
1008
1009
  }
  
  /*
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
1010
1011
1012
1013
1014
1015
1016
   * Inode and filehandle revalidation for lookups.
   *
   * We force revalidation in the cases where the VFS sets LOOKUP_REVAL,
   * or if the intent information indicates that we're about to open this
   * particular file and the "nocto" mount flag is not set.
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1017
1018
1019
1020
  static inline
  int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd)
  {
  	struct nfs_server *server = NFS_SERVER(inode);
36d43a437   David Howells   NFS: Use d_automo...
1021
  	if (IS_AUTOMOUNT(inode))
4e99a1ff3   Trond Myklebust   NFS: Fix dentry r...
1022
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
  	if (nd != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1024
  		/* VFS wants an on-the-wire revalidation */
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
1025
  		if (nd->flags & LOOKUP_REVAL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
1027
  			goto out_force;
  		/* This is an open(2) */
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
1028
  		if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 &&
4e0641a7a   Trond Myklebust   NFS: Optimise awa...
1029
1030
1031
  				!(server->flags & NFS_MOUNT_NOCTO) &&
  				(S_ISREG(inode->i_mode) ||
  				 S_ISDIR(inode->i_mode)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032
  			goto out_force;
4f48af458   Trond Myklebust   NFS: Simplify fil...
1033
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
  	}
  	return nfs_revalidate_inode(server, inode);
  out_force:
  	return __nfs_revalidate_inode(server, inode);
  }
  
  /*
   * We judge how long we want to trust negative
   * dentries by looking at the parent inode mtime.
   *
   * If parent mtime has changed, we revalidate, else we wait for a
   * period corresponding to the parent's attribute cache timeout value.
   */
  static inline
  int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
  		       struct nameidata *nd)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
  	/* Don't revalidate a negative dentry if we're creating a new file */
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
1052
  	if (nd != NULL && nfs_lookup_check_intent(nd, LOOKUP_CREATE) != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1053
  		return 0;
4eec952e4   Trond Myklebust   NFS: Add options ...
1054
1055
  	if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG)
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
  	return !nfs_check_verifier(dir, dentry);
  }
  
  /*
   * This is called every time the dcache has a lookup hit,
   * and we should check whether we can really trust that
   * lookup.
   *
   * NOTE! The hit can be a negative hit too, don't assume
   * we have an inode!
   *
   * If the parent directory is seen to have changed, we throw out the
   * cached dentry and do a new lookup.
   */
34286d666   Nick Piggin   fs: rcu-walk awar...
1070
  static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
1072
1073
1074
  {
  	struct inode *dir;
  	struct inode *inode;
  	struct dentry *parent;
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1075
1076
  	struct nfs_fh *fhandle = NULL;
  	struct nfs_fattr *fattr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1077
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078

34286d666   Nick Piggin   fs: rcu-walk awar...
1079
1080
  	if (nd->flags & LOOKUP_RCU)
  		return -ECHILD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1081
  	parent = dget_parent(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
  	dir = parent->d_inode;
91d5b4702   Chuck Lever   NFS: add I/O perf...
1083
  	nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084
1085
1086
1087
1088
1089
1090
1091
1092
  	inode = dentry->d_inode;
  
  	if (!inode) {
  		if (nfs_neg_need_reval(dir, dentry, nd))
  			goto out_bad;
  		goto out_valid;
  	}
  
  	if (is_bad_inode(inode)) {
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1093
1094
  		dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
1095
  				__func__, dentry->d_parent->d_name.name,
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1096
  				dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
1098
  		goto out_bad;
  	}
15860ab1d   Trond Myklebust   NFSv4: Ensure tha...
1099
1100
  	if (nfs_have_delegation(inode, FMODE_READ))
  		goto out_set_verifier;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1101
  	/* Force a full look up iff the parent directory has changed */
a12802cab   Trond Myklebust   NFS: Be strict ab...
1102
  	if (!nfs_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
1104
1105
1106
1107
1108
1109
  		if (nfs_lookup_verify_inode(inode, nd))
  			goto out_zap_parent;
  		goto out_valid;
  	}
  
  	if (NFS_STALE(inode))
  		goto out_bad;
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1110
1111
1112
1113
1114
  	error = -ENOMEM;
  	fhandle = nfs_alloc_fhandle();
  	fattr = nfs_alloc_fattr();
  	if (fhandle == NULL || fattr == NULL)
  		goto out_error;
7c5130588   Bryan Schumaker   NFS: lookup suppo...
1115
  	error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1116
1117
  	if (error)
  		goto out_bad;
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1118
  	if (nfs_compare_fh(NFS_FH(inode), fhandle))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1119
  		goto out_bad;
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1120
  	if ((error = nfs_refresh_inode(inode, fattr)) != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
  		goto out_bad;
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1122
1123
  	nfs_free_fattr(fattr);
  	nfs_free_fhandle(fhandle);
15860ab1d   Trond Myklebust   NFSv4: Ensure tha...
1124
  out_set_verifier:
cf8ba45e0   Trond Myklebust   NFS: don't cache ...
1125
  	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1126
   out_valid:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1127
  	dput(parent);
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1128
1129
  	dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
1130
  			__func__, dentry->d_parent->d_name.name,
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1131
  			dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1132
1133
1134
1135
  	return 1;
  out_zap_parent:
  	nfs_zap_caches(dir);
   out_bad:
a1643a92f   Trond Myklebust   NFS: NFS_CACHEINV...
1136
  	nfs_mark_for_revalidate(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1137
1138
1139
1140
1141
1142
  	if (inode && S_ISDIR(inode->i_mode)) {
  		/* Purge readdir caches. */
  		nfs_zap_caches(inode);
  		/* If we have submounts, don't unhash ! */
  		if (have_submounts(dentry))
  			goto out_valid;
d9e80b7de   Al Viro   nfs d_revalidate(...
1143
1144
  		if (dentry->d_flags & DCACHE_DISCONNECTED)
  			goto out_valid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1145
1146
1147
  		shrink_dcache_parent(dentry);
  	}
  	d_drop(dentry);
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1148
1149
  	nfs_free_fattr(fattr);
  	nfs_free_fhandle(fhandle);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1150
  	dput(parent);
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1151
1152
  	dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
1153
  			__func__, dentry->d_parent->d_name.name,
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1154
  			dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1155
  	return 0;
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1156
1157
1158
1159
1160
1161
1162
1163
1164
  out_error:
  	nfs_free_fattr(fattr);
  	nfs_free_fhandle(fhandle);
  	dput(parent);
  	dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d
  ",
  			__func__, dentry->d_parent->d_name.name,
  			dentry->d_name.name, error);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165
1166
1167
1168
1169
  }
  
  /*
   * This is called from dput() when d_count is going to 0.
   */
fe15ce446   Nick Piggin   fs: change d_dele...
1170
  static int nfs_dentry_delete(const struct dentry *dentry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1171
1172
1173
1174
1175
  {
  	dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)
  ",
  		dentry->d_parent->d_name.name, dentry->d_name.name,
  		dentry->d_flags);
77f111929   Trond Myklebust   NFS: Ensure that ...
1176
1177
1178
  	/* Unhash any dentry with a stale inode */
  	if (dentry->d_inode != NULL && NFS_STALE(dentry->d_inode))
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
  	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
  		/* Unhash it, so that ->d_iput() would be called */
  		return 1;
  	}
  	if (!(dentry->d_sb->s_flags & MS_ACTIVE)) {
  		/* Unhash it, so that ancestors of killed async unlink
  		 * files will be cleaned up during umount */
  		return 1;
  	}
  	return 0;
  
  }
1b83d7070   Trond Myklebust   NFS: Protect inod...
1191
1192
1193
1194
1195
1196
1197
  static void nfs_drop_nlink(struct inode *inode)
  {
  	spin_lock(&inode->i_lock);
  	if (inode->i_nlink > 0)
  		drop_nlink(inode);
  	spin_unlock(&inode->i_lock);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1198
1199
1200
1201
1202
1203
  /*
   * Called when the dentry loses inode.
   * We use it to clean up silly-renamed files.
   */
  static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
  {
83672d392   Neil Brown   NFS: Fix director...
1204
1205
1206
  	if (S_ISDIR(inode->i_mode))
  		/* drop any readdir cache as it could easily be old */
  		NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207
  	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
9a53c3a78   Dave Hansen   [PATCH] r/o bind ...
1208
  		drop_nlink(inode);
e4eff1a62   Trond Myklebust   SUNRPC: Clean up ...
1209
  		nfs_complete_unlink(dentry, inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1210
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1211
1212
  	iput(inode);
  }
b1942c5f8   Al Viro   nfs: store devnam...
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
  static void nfs_d_release(struct dentry *dentry)
  {
  	/* free cached devname value, if it survived that far */
  	if (unlikely(dentry->d_fsdata)) {
  		if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
  			WARN_ON(1);
  		else
  			kfree(dentry->d_fsdata);
  	}
  }
f786aa90e   Al Viro   constify dentry_o...
1223
  const struct dentry_operations nfs_dentry_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1224
1225
1226
  	.d_revalidate	= nfs_lookup_revalidate,
  	.d_delete	= nfs_dentry_delete,
  	.d_iput		= nfs_dentry_iput,
36d43a437   David Howells   NFS: Use d_automo...
1227
  	.d_automount	= nfs_d_automount,
b1942c5f8   Al Viro   nfs: store devnam...
1228
  	.d_release	= nfs_d_release,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1229
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1230
1231
1232
  static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
  {
  	struct dentry *res;
565277f63   Trond Myklebust   NFS: Fix a race i...
1233
  	struct dentry *parent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1234
  	struct inode *inode = NULL;
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1235
1236
  	struct nfs_fh *fhandle = NULL;
  	struct nfs_fattr *fattr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1237
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1238
1239
1240
1241
  
  	dfprintk(VFS, "NFS: lookup(%s/%s)
  ",
  		dentry->d_parent->d_name.name, dentry->d_name.name);
91d5b4702   Chuck Lever   NFS: add I/O perf...
1242
  	nfs_inc_stats(dir, NFSIOS_VFSLOOKUP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
1244
1245
1246
  
  	res = ERR_PTR(-ENAMETOOLONG);
  	if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
  		goto out;
fd6840714   Trond Myklebust   NFS: nfs_lookup -...
1247
1248
1249
1250
1251
1252
1253
  	/*
  	 * If we're doing an exclusive create, optimize away the lookup
  	 * but don't hash the dentry.
  	 */
  	if (nfs_is_exclusive_create(dir, nd)) {
  		d_instantiate(dentry, NULL);
  		res = NULL;
fc0f684c2   Trond Myklebust   NFS: Remove BKL f...
1254
  		goto out;
fd6840714   Trond Myklebust   NFS: nfs_lookup -...
1255
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1256

e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1257
1258
1259
1260
1261
  	res = ERR_PTR(-ENOMEM);
  	fhandle = nfs_alloc_fhandle();
  	fattr = nfs_alloc_fattr();
  	if (fhandle == NULL || fattr == NULL)
  		goto out;
565277f63   Trond Myklebust   NFS: Fix a race i...
1262
1263
1264
  	parent = dentry->d_parent;
  	/* Protect against concurrent sillydeletes */
  	nfs_block_sillyrename(parent);
7c5130588   Bryan Schumaker   NFS: lookup suppo...
1265
  	error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1266
1267
1268
1269
  	if (error == -ENOENT)
  		goto no_entry;
  	if (error < 0) {
  		res = ERR_PTR(error);
565277f63   Trond Myklebust   NFS: Fix a race i...
1270
  		goto out_unblock_sillyrename;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1271
  	}
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1272
  	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
bf0c84f16   Namhyung Kim   NFS: use ERR_CAST()
1273
  	res = ERR_CAST(inode);
03f28e3a2   Trond Myklebust   NFS: Make nfs_fhg...
1274
  	if (IS_ERR(res))
565277f63   Trond Myklebust   NFS: Fix a race i...
1275
  		goto out_unblock_sillyrename;
54ceac451   David Howells   NFS: Share NFS su...
1276

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1277
  no_entry:
54ceac451   David Howells   NFS: Share NFS su...
1278
  	res = d_materialise_unique(dentry, inode);
9eaef27b3   Trond Myklebust   [PATCH] VFS: Make...
1279
1280
  	if (res != NULL) {
  		if (IS_ERR(res))
565277f63   Trond Myklebust   NFS: Fix a race i...
1281
  			goto out_unblock_sillyrename;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1282
  		dentry = res;
9eaef27b3   Trond Myklebust   [PATCH] VFS: Make...
1283
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1284
  	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
565277f63   Trond Myklebust   NFS: Fix a race i...
1285
1286
  out_unblock_sillyrename:
  	nfs_unblock_sillyrename(parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
  out:
e1fb4d05d   Trond Myklebust   NFS: Reduce the s...
1288
1289
  	nfs_free_fattr(fattr);
  	nfs_free_fhandle(fhandle);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1290
1291
1292
1293
1294
  	return res;
  }
  
  #ifdef CONFIG_NFS_V4
  static int nfs_open_revalidate(struct dentry *, struct nameidata *);
f786aa90e   Al Viro   constify dentry_o...
1295
  const struct dentry_operations nfs4_dentry_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1296
1297
1298
  	.d_revalidate	= nfs_open_revalidate,
  	.d_delete	= nfs_dentry_delete,
  	.d_iput		= nfs_dentry_iput,
36d43a437   David Howells   NFS: Use d_automo...
1299
  	.d_automount	= nfs_d_automount,
b1942c5f8   Al Viro   nfs: store devnam...
1300
  	.d_release	= nfs_d_release,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1301
  };
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
1302
1303
1304
1305
  /*
   * Use intent information to determine whether we need to substitute
   * the NFSv4-style stateful OPEN for the LOOKUP call
   */
5584c3063   Trond Myklebust   NFSv4: Clean up i...
1306
  static int is_atomic_open(struct nameidata *nd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1307
  {
1d6757fbf   Trond Myklebust   [PATCH] NFS: Fix ...
1308
  	if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1309
1310
1311
1312
1313
  		return 0;
  	/* NFS does not (yet) have a stateful open for directories */
  	if (nd->flags & LOOKUP_DIRECTORY)
  		return 0;
  	/* Are we trying to write to a read only partition? */
2c463e954   Dave Hansen   [PATCH] r/o bind ...
1314
  	if (__mnt_is_readonly(nd->path.mnt) &&
8a5e929dd   Al Viro   don't translitera...
1315
  	    (nd->intent.open.flags & (O_CREAT|O_TRUNC|O_ACCMODE)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1316
1317
1318
  		return 0;
  	return 1;
  }
8a5e929dd   Al Viro   don't translitera...
1319
1320
1321
1322
1323
1324
1325
1326
1327
  static fmode_t flags_to_mode(int flags)
  {
  	fmode_t res = (__force fmode_t)flags & FMODE_EXEC;
  	if ((flags & O_ACCMODE) != O_WRONLY)
  		res |= FMODE_READ;
  	if ((flags & O_ACCMODE) != O_RDONLY)
  		res |= FMODE_WRITE;
  	return res;
  }
511415980   Al Viro   nameidata_to_nfs_...
1328
  static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags)
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1329
  {
5ede7b1cf   Al Viro   pull manipulation...
1330
  	return alloc_nfs_open_context(dentry, flags_to_mode(open_flags));
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
  }
  
  static int do_open(struct inode *inode, struct file *filp)
  {
  	nfs_fscache_set_inode_cookie(inode, filp);
  	return 0;
  }
  
  static int nfs_intent_set_file(struct nameidata *nd, struct nfs_open_context *ctx)
  {
  	struct file *filp;
  	int ret = 0;
  
  	/* If the open_intent is for execute, we have an extra check to make */
  	if (ctx->mode & FMODE_EXEC) {
3d4ff43d8   Al Viro   nfs_open_context ...
1346
  		ret = nfs_may_open(ctx->dentry->d_inode,
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1347
1348
1349
1350
1351
  				ctx->cred,
  				nd->intent.open.flags);
  		if (ret < 0)
  			goto out;
  	}
3d4ff43d8   Al Viro   nfs_open_context ...
1352
  	filp = lookup_instantiate_filp(nd, ctx->dentry, do_open);
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1353
1354
1355
1356
1357
1358
1359
1360
  	if (IS_ERR(filp))
  		ret = PTR_ERR(filp);
  	else
  		nfs_file_set_open_context(filp, ctx);
  out:
  	put_nfs_open_context(ctx);
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1361
1362
  static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
  {
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1363
1364
  	struct nfs_open_context *ctx;
  	struct iattr attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1365
  	struct dentry *res = NULL;
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1366
  	struct inode *inode;
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1367
  	int open_flags;
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1368
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1369

1e7cb3dc1   Chuck Lever   NFS: directory tr...
1370
1371
1372
  	dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s
  ",
  			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1373
  	/* Check that we are indeed trying to open this file */
5584c3063   Trond Myklebust   NFSv4: Clean up i...
1374
  	if (!is_atomic_open(nd))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1375
1376
1377
1378
1379
1380
  		goto no_open;
  
  	if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
  		res = ERR_PTR(-ENAMETOOLONG);
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381

d4d9cdcb4   Trond Myklebust   NFS: Don't hash t...
1382
1383
  	/* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash
  	 * the dentry. */
3516586a4   Al Viro   [PATCH] make O_EX...
1384
  	if (nd->flags & LOOKUP_EXCL) {
d4d9cdcb4   Trond Myklebust   NFS: Don't hash t...
1385
  		d_instantiate(dentry, NULL);
02a913a73   Trond Myklebust   NFSv4: Eliminate ...
1386
1387
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1388

511415980   Al Viro   nameidata_to_nfs_...
1389
1390
1391
  	open_flags = nd->intent.open.flags;
  
  	ctx = create_nfs_open_context(dentry, open_flags);
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1392
1393
1394
  	res = ERR_CAST(ctx);
  	if (IS_ERR(ctx))
  		goto out;
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1395
1396
1397
  	if (nd->flags & LOOKUP_CREATE) {
  		attr.ia_mode = nd->intent.open.create_mode;
  		attr.ia_valid = ATTR_MODE;
a8a5da996   Aneesh Kumar K.V   nfs: Set MS_POSIX...
1398
  		attr.ia_mode &= ~current_umask();
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1399
  	} else {
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1400
  		open_flags &= ~(O_EXCL | O_CREAT);
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1401
  		attr.ia_valid = 0;
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1402
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1403
  	/* Open the file on the server */
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1404
  	nfs_block_sillyrename(dentry->d_parent);
2b484297e   Trond Myklebust   NFS: Add an 'open...
1405
  	inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1406
1407
  	if (IS_ERR(inode)) {
  		nfs_unblock_sillyrename(dentry->d_parent);
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1408
  		put_nfs_open_context(ctx);
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1409
  		switch (PTR_ERR(inode)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410
1411
  			/* Make a negative dentry */
  			case -ENOENT:
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1412
  				d_add(dentry, NULL);
02a913a73   Trond Myklebust   NFSv4: Eliminate ...
1413
1414
  				res = NULL;
  				goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1415
  			/* This turned out not to be a regular file */
1788ea6e3   Jeff Layton   nfs: when attempt...
1416
  			case -EISDIR:
6f926b5ba   Trond Myklebust   [NFS]: Check that...
1417
1418
  			case -ENOTDIR:
  				goto no_open;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1419
1420
1421
  			case -ELOOP:
  				if (!(nd->intent.open.flags & O_NOFOLLOW))
  					goto no_open;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422
1423
  			/* case -EINVAL: */
  			default:
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1424
  				res = ERR_CAST(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
1426
  				goto out;
  		}
cd9a1c0e5   Trond Myklebust   NFSv4: Clean up n...
1427
  	}
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1428
  	res = d_add_unique(dentry, inode);
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1429
  	nfs_unblock_sillyrename(dentry->d_parent);
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1430
  	if (res != NULL) {
3d4ff43d8   Al Viro   nfs_open_context ...
1431
1432
  		dput(ctx->dentry);
  		ctx->dentry = dget(res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
  		dentry = res;
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1434
  	}
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1435
1436
1437
1438
1439
1440
  	err = nfs_intent_set_file(nd, ctx);
  	if (err < 0) {
  		if (res != NULL)
  			dput(res);
  		return ERR_PTR(err);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1441
  out:
f46e0bd34   Trond Myklebust   NFSv4: Further mi...
1442
  	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1443
1444
1445
1446
1447
1448
1449
1450
  	return res;
  no_open:
  	return nfs_lookup(dir, dentry, nd);
  }
  
  static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
  {
  	struct dentry *parent = NULL;
657e94b67   Nick Piggin   nfs: add missing ...
1451
  	struct inode *inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1452
  	struct inode *dir;
b8d4caddd   Trond Myklebust   NFSv4: Clean up n...
1453
  	struct nfs_open_context *ctx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1454
  	int openflags, ret = 0;
657e94b67   Nick Piggin   nfs: add missing ...
1455
1456
1457
1458
  	if (nd->flags & LOOKUP_RCU)
  		return -ECHILD;
  
  	inode = dentry->d_inode;
1f063d2cd   Trond Myklebust   NFSv4: Don't atte...
1459
  	if (!is_atomic_open(nd) || d_mountpoint(dentry))
5584c3063   Trond Myklebust   NFSv4: Clean up i...
1460
  		goto no_open;
2b484297e   Trond Myklebust   NFS: Add an 'open...
1461

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1462
1463
  	parent = dget_parent(dentry);
  	dir = parent->d_inode;
2b484297e   Trond Myklebust   NFS: Add an 'open...
1464

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1465
1466
1467
  	/* We can't create new files in nfs_open_revalidate(), so we
  	 * optimize away revalidation of negative dentries.
  	 */
216d5d068   Trond Myklebust   NFSv4: Use NFSv2/...
1468
1469
1470
  	if (inode == NULL) {
  		if (!nfs_neg_need_reval(dir, dentry, nd))
  			ret = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1471
  		goto out;
216d5d068   Trond Myklebust   NFSv4: Use NFSv2/...
1472
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1473
1474
  	/* NFS only supports OPEN on regular files */
  	if (!S_ISREG(inode->i_mode))
5584c3063   Trond Myklebust   NFSv4: Clean up i...
1475
  		goto no_open_dput;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1476
1477
1478
  	openflags = nd->intent.open.flags;
  	/* We cannot do exclusive creation on a positive dentry */
  	if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
5584c3063   Trond Myklebust   NFSv4: Clean up i...
1479
  		goto no_open_dput;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1480
  	/* We can't create new files, or truncate existing ones here */
0a377cff9   Trond Myklebust   NFS: Fix an Oops ...
1481
  	openflags &= ~(O_CREAT|O_EXCL|O_TRUNC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1482

511415980   Al Viro   nameidata_to_nfs_...
1483
  	ctx = create_nfs_open_context(dentry, openflags);
b8d4caddd   Trond Myklebust   NFSv4: Clean up n...
1484
1485
1486
  	ret = PTR_ERR(ctx);
  	if (IS_ERR(ctx))
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1487
  	/*
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
1488
  	 * Note: we're not holding inode->i_mutex and so may be racing with
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1489
1490
1491
  	 * operations that change the directory. We therefore save the
  	 * change attribute *before* we do the RPC call.
  	 */
2b484297e   Trond Myklebust   NFS: Add an 'open...
1492
  	inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, NULL);
535918f14   Trond Myklebust   NFSv4: Further cl...
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
  	if (IS_ERR(inode)) {
  		ret = PTR_ERR(inode);
  		switch (ret) {
  		case -EPERM:
  		case -EACCES:
  		case -EDQUOT:
  		case -ENOSPC:
  		case -EROFS:
  			goto out_put_ctx;
  		default:
  			goto out_drop;
  		}
  	}
  	iput(inode);
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1507
  	if (inode != dentry->d_inode)
535918f14   Trond Myklebust   NFSv4: Further cl...
1508
  		goto out_drop;
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1509
1510
1511
1512
1513
  
  	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  	ret = nfs_intent_set_file(nd, ctx);
  	if (ret >= 0)
  		ret = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1514
1515
  out:
  	dput(parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1516
  	return ret;
535918f14   Trond Myklebust   NFSv4: Further cl...
1517
1518
1519
1520
1521
1522
  out_drop:
  	d_drop(dentry);
  	ret = 0;
  out_put_ctx:
  	put_nfs_open_context(ctx);
  	goto out;
5584c3063   Trond Myklebust   NFSv4: Clean up i...
1523
  no_open_dput:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1524
  	dput(parent);
5584c3063   Trond Myklebust   NFSv4: Clean up i...
1525
  no_open:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1526
1527
  	return nfs_lookup_revalidate(dentry, nd);
  }
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1528

4acdaf27e   Al Viro   switch ->create()...
1529
1530
  static int nfs_open_create(struct inode *dir, struct dentry *dentry,
  		umode_t mode, struct nameidata *nd)
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1531
1532
1533
1534
  {
  	struct nfs_open_context *ctx = NULL;
  	struct iattr attr;
  	int error;
8a5e929dd   Al Viro   don't translitera...
1535
  	int open_flags = O_CREAT|O_EXCL;
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1536
1537
1538
1539
1540
1541
1542
  
  	dfprintk(VFS, "NFS: create(%s/%ld), %s
  ",
  			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
  
  	attr.ia_mode = mode;
  	attr.ia_valid = ATTR_MODE;
dd7dd556e   Al Viro   no need to check ...
1543
  	if (nd)
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1544
  		open_flags = nd->intent.open.flags;
f7c85868f   Al Viro   fix mknod() on nf...
1545
1546
1547
1548
  	ctx = create_nfs_open_context(dentry, open_flags);
  	error = PTR_ERR(ctx);
  	if (IS_ERR(ctx))
  		goto out_err_drop;
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1549
1550
1551
1552
  
  	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, ctx);
  	if (error != 0)
  		goto out_put_ctx;
dd7dd556e   Al Viro   no need to check ...
1553
  	if (nd) {
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1554
1555
1556
  		error = nfs_intent_set_file(nd, ctx);
  		if (error < 0)
  			goto out_err;
f7c85868f   Al Viro   fix mknod() on nf...
1557
1558
  	} else {
  		put_nfs_open_context(ctx);
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1559
  	}
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1560
1561
  	return 0;
  out_put_ctx:
f7c85868f   Al Viro   fix mknod() on nf...
1562
  	put_nfs_open_context(ctx);
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1563
  out_err_drop:
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1564
  	d_drop(dentry);
898f635c4   Trond Myklebust   NFSv4: Don't igno...
1565
  out_err:
c0204fd2b   Trond Myklebust   NFS: Clean up nfs...
1566
1567
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1568
  #endif /* CONFIG_NFSV4 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1569
1570
1571
1572
1573
1574
  /*
   * Code common to create, mkdir, and mknod.
   */
  int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
  				struct nfs_fattr *fattr)
  {
fab728e15   Trond Myklebust   NFS: Ensure nfs_i...
1575
1576
  	struct dentry *parent = dget_parent(dentry);
  	struct inode *dir = parent->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1577
1578
  	struct inode *inode;
  	int error = -EACCES;
fab728e15   Trond Myklebust   NFS: Ensure nfs_i...
1579
  	d_drop(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1580
1581
  	/* We may have been initialized further down */
  	if (dentry->d_inode)
fab728e15   Trond Myklebust   NFS: Ensure nfs_i...
1582
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1583
  	if (fhandle->size == 0) {
7c5130588   Bryan Schumaker   NFS: lookup suppo...
1584
  		error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1585
  		if (error)
fab728e15   Trond Myklebust   NFS: Ensure nfs_i...
1586
  			goto out_error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1587
  	}
5724ab378   Trond Myklebust   NFS: nfs_instanti...
1588
  	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1589
1590
  	if (!(fattr->valid & NFS_ATTR_FATTR)) {
  		struct nfs_server *server = NFS_SB(dentry->d_sb);
8fa5c000d   David Howells   NFS: Move rpc_ops...
1591
  		error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1592
  		if (error < 0)
fab728e15   Trond Myklebust   NFS: Ensure nfs_i...
1593
  			goto out_error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1594
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1595
  	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
03f28e3a2   Trond Myklebust   NFS: Make nfs_fhg...
1596
1597
  	error = PTR_ERR(inode);
  	if (IS_ERR(inode))
fab728e15   Trond Myklebust   NFS: Ensure nfs_i...
1598
1599
1600
1601
  		goto out_error;
  	d_add(dentry, inode);
  out:
  	dput(parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1602
  	return 0;
fab728e15   Trond Myklebust   NFS: Ensure nfs_i...
1603
1604
1605
1606
  out_error:
  	nfs_mark_for_revalidate(dir);
  	dput(parent);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1607
1608
1609
1610
1611
1612
1613
1614
  }
  
  /*
   * Following a failed create operation, we drop the dentry rather
   * than retain a negative dentry. This avoids a problem in the event
   * that the operation succeeded on the server, but an error in the
   * reply path made it appear to have failed.
   */
4acdaf27e   Al Viro   switch ->create()...
1615
1616
  static int nfs_create(struct inode *dir, struct dentry *dentry,
  		umode_t mode, struct nameidata *nd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1617
1618
1619
  {
  	struct iattr attr;
  	int error;
8a5e929dd   Al Viro   don't translitera...
1620
  	int open_flags = O_CREAT|O_EXCL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1621

1e7cb3dc1   Chuck Lever   NFS: directory tr...
1622
1623
1624
  	dfprintk(VFS, "NFS: create(%s/%ld), %s
  ",
  			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1625
1626
1627
  
  	attr.ia_mode = mode;
  	attr.ia_valid = ATTR_MODE;
dd7dd556e   Al Viro   no need to check ...
1628
  	if (nd)
8a0eebf66   Trond Myklebust   NFS: Fix NFSv3 ex...
1629
1630
1631
  		open_flags = nd->intent.open.flags;
  
  	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1632
1633
  	if (error != 0)
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1634
1635
  	return 0;
  out_err:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1636
1637
1638
1639
1640
1641
1642
1643
  	d_drop(dentry);
  	return error;
  }
  
  /*
   * See comments for nfs_proc_create regarding failed operations.
   */
  static int
1a67aafb5   Al Viro   switch ->mknod() ...
1644
  nfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1645
1646
1647
  {
  	struct iattr attr;
  	int status;
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1648
1649
1650
  	dfprintk(VFS, "NFS: mknod(%s/%ld), %s
  ",
  			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1651
1652
1653
1654
1655
1656
  
  	if (!new_valid_dev(rdev))
  		return -EINVAL;
  
  	attr.ia_mode = mode;
  	attr.ia_valid = ATTR_MODE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1657
  	status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1658
1659
  	if (status != 0)
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1660
1661
  	return 0;
  out_err:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1662
1663
1664
1665
1666
1667
1668
  	d_drop(dentry);
  	return status;
  }
  
  /*
   * See comments for nfs_proc_create regarding failed operations.
   */
18bb1db3e   Al Viro   switch vfs_mkdir(...
1669
  static int nfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1670
1671
1672
  {
  	struct iattr attr;
  	int error;
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1673
1674
1675
  	dfprintk(VFS, "NFS: mkdir(%s/%ld), %s
  ",
  			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1676
1677
1678
  
  	attr.ia_valid = ATTR_MODE;
  	attr.ia_mode = mode | S_IFDIR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1679
  	error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1680
1681
  	if (error != 0)
  		goto out_err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1682
1683
1684
  	return 0;
  out_err:
  	d_drop(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1685
1686
  	return error;
  }
d45b9d8ba   Trond Myklebust   NFS: Handle -ENOE...
1687
1688
1689
1690
1691
  static void nfs_dentry_handle_enoent(struct dentry *dentry)
  {
  	if (dentry->d_inode != NULL && !d_unhashed(dentry))
  		d_delete(dentry);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1692
1693
1694
  static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
  {
  	int error;
1e7cb3dc1   Chuck Lever   NFS: directory tr...
1695
1696
1697
  	dfprintk(VFS, "NFS: rmdir(%s/%ld), %s
  ",
  			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1698

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1699
1700
1701
  	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
  	/* Ensure the VFS deletes this inode */
  	if (error == 0 && dentry->d_inode != NULL)
ce71ec368   Dave Hansen   [PATCH] r/o bind ...
1702
  		clear_nlink(dentry->d_inode);
d45b9d8ba   Trond Myklebust   NFS: Handle -ENOE...
1703
1704
  	else if (error == -ENOENT)
  		nfs_dentry_handle_enoent(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1705
1706
1707
  
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
  /*
   * Remove a file after making sure there are no pending writes,
   * and after checking that the file has only one user. 
   *
   * We invalidate the attribute cache and free the inode prior to the operation
   * to avoid possible races if the server reuses the inode.
   */
  static int nfs_safe_remove(struct dentry *dentry)
  {
  	struct inode *dir = dentry->d_parent->d_inode;
  	struct inode *inode = dentry->d_inode;
  	int error = -EBUSY;
  		
  	dfprintk(VFS, "NFS: safe_remove(%s/%s)
  ",
  		dentry->d_parent->d_name.name, dentry->d_name.name);
  
  	/* If the dentry was sillyrenamed, we simply call d_delete() */
  	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
  		error = 0;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1730
  	if (inode != NULL) {
cae7a073a   Trond Myklebust   NFSv4: Return del...
1731
  		nfs_inode_return_delegation(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1732
1733
1734
  		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
  		/* The VFS may want to delete this inode */
  		if (error == 0)
1b83d7070   Trond Myklebust   NFS: Protect inod...
1735
  			nfs_drop_nlink(inode);
5ba7cc480   Trond Myklebust   NFS: Fix post-op ...
1736
  		nfs_mark_for_revalidate(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1737
1738
  	} else
  		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
d45b9d8ba   Trond Myklebust   NFS: Handle -ENOE...
1739
1740
  	if (error == -ENOENT)
  		nfs_dentry_handle_enoent(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
  out:
  	return error;
  }
  
  /*  We do silly rename. In case sillyrename() returns -EBUSY, the inode
   *  belongs to an active ".nfs..." file and we return -EBUSY.
   *
   *  If sillyrename() returns 0, we do nothing, otherwise we unlink.
   */
  static int nfs_unlink(struct inode *dir, struct dentry *dentry)
  {
  	int error;
  	int need_rehash = 0;
  
  	dfprintk(VFS, "NFS: unlink(%s/%ld, %s)
  ", dir->i_sb->s_id,
  		dir->i_ino, dentry->d_name.name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1758
  	spin_lock(&dentry->d_lock);
b7ab39f63   Nick Piggin   fs: dcache scale ...
1759
  	if (dentry->d_count > 1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1760
  		spin_unlock(&dentry->d_lock);
ccfeb5062   Trond Myklebust   NFS: Fix up "rm -...
1761
1762
  		/* Start asynchronous writeout of the inode */
  		write_inode_now(dentry->d_inode, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1763
  		error = nfs_sillyrename(dir, dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1764
1765
1766
1767
1768
1769
1770
  		return error;
  	}
  	if (!d_unhashed(dentry)) {
  		__d_drop(dentry);
  		need_rehash = 1;
  	}
  	spin_unlock(&dentry->d_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1771
  	error = nfs_safe_remove(dentry);
d45b9d8ba   Trond Myklebust   NFS: Handle -ENOE...
1772
  	if (!error || error == -ENOENT) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1773
1774
1775
  		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  	} else if (need_rehash)
  		d_rehash(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1776
1777
  	return error;
  }
873101b33   Chuck Lever   NFS: copy symlink...
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
  /*
   * To create a symbolic link, most file systems instantiate a new inode,
   * add a page to it containing the path, then write it out to the disk
   * using prepare_write/commit_write.
   *
   * Unfortunately the NFS client can't create the in-core inode first
   * because it needs a file handle to create an in-core inode (see
   * fs/nfs/inode.c:nfs_fhget).  We only have a file handle *after* the
   * symlink request has completed on the server.
   *
   * So instead we allocate a raw page, copy the symname into it, then do
   * the SYMLINK request with the page as the buffer.  If it succeeds, we
   * now have a new file handle and can instantiate an in-core NFS inode
   * and move the raw page into its mapping.
   */
  static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1794
  {
873101b33   Chuck Lever   NFS: copy symlink...
1795
1796
1797
  	struct pagevec lru_pvec;
  	struct page *page;
  	char *kaddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1798
  	struct iattr attr;
873101b33   Chuck Lever   NFS: copy symlink...
1799
  	unsigned int pathlen = strlen(symname);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1800
1801
1802
1803
1804
  	int error;
  
  	dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)
  ", dir->i_sb->s_id,
  		dir->i_ino, dentry->d_name.name, symname);
873101b33   Chuck Lever   NFS: copy symlink...
1805
1806
  	if (pathlen > PAGE_SIZE)
  		return -ENAMETOOLONG;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1807

873101b33   Chuck Lever   NFS: copy symlink...
1808
1809
  	attr.ia_mode = S_IFLNK | S_IRWXUGO;
  	attr.ia_valid = ATTR_MODE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1810

83d93f222   Jeff Layton   NFS: Use GFP_HIGH...
1811
  	page = alloc_page(GFP_HIGHUSER);
76566991f   Trond Myklebust   NFS: Remove BKL f...
1812
  	if (!page)
873101b33   Chuck Lever   NFS: copy symlink...
1813
  		return -ENOMEM;
873101b33   Chuck Lever   NFS: copy symlink...
1814
1815
1816
1817
1818
1819
  
  	kaddr = kmap_atomic(page, KM_USER0);
  	memcpy(kaddr, symname, pathlen);
  	if (pathlen < PAGE_SIZE)
  		memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
  	kunmap_atomic(kaddr, KM_USER0);
94a6d7532   Chuck Lever   NFS: Use cached p...
1820
  	error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
873101b33   Chuck Lever   NFS: copy symlink...
1821
1822
1823
1824
1825
  	if (error != 0) {
  		dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d
  ",
  			dir->i_sb->s_id, dir->i_ino,
  			dentry->d_name.name, symname, error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1826
  		d_drop(dentry);
873101b33   Chuck Lever   NFS: copy symlink...
1827
  		__free_page(page);
873101b33   Chuck Lever   NFS: copy symlink...
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
  		return error;
  	}
  
  	/*
  	 * No big deal if we can't add this page to the page cache here.
  	 * READLINK will get the missing page from the server if needed.
  	 */
  	pagevec_init(&lru_pvec, 0);
  	if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0,
  							GFP_KERNEL)) {
39cf8a137   Chuck Lever   [PATCH] NFS: fix ...
1838
  		pagevec_add(&lru_pvec, page);
4f98a2fee   Rik van Riel   vmscan: split LRU...
1839
  		pagevec_lru_add_file(&lru_pvec);
873101b33   Chuck Lever   NFS: copy symlink...
1840
1841
1842
1843
  		SetPageUptodate(page);
  		unlock_page(page);
  	} else
  		__free_page(page);
873101b33   Chuck Lever   NFS: copy symlink...
1844
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
  }
  
  static int 
  nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
  {
  	struct inode *inode = old_dentry->d_inode;
  	int error;
  
  	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)
  ",
  		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
  		dentry->d_parent->d_name.name, dentry->d_name.name);
9a3936aac   Trond Myklebust   NFSv4: The link()...
1857
  	nfs_inode_return_delegation(inode);
9697d2342   Trond Myklebust   NFS: Ensure that ...
1858
  	d_drop(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1859
  	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
cf8095561   Trond Myklebust   NFS: Ensure that ...
1860
  	if (error == 0) {
7de9c6ee3   Al Viro   new helper: ihold()
1861
  		ihold(inode);
9697d2342   Trond Myklebust   NFS: Ensure that ...
1862
  		d_add(dentry, inode);
cf8095561   Trond Myklebust   NFS: Ensure that ...
1863
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
  	return error;
  }
  
  /*
   * RENAME
   * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
   * different file handle for the same inode after a rename (e.g. when
   * moving to a different directory). A fail-safe method to do so would
   * be to look up old_dir/old_name, create a link to new_dir/new_name and
   * rename the old file using the sillyrename stuff. This way, the original
   * file in old_dir will go away when the last process iput()s the inode.
   *
   * FIXED.
   * 
   * It actually works quite well. One needs to have the possibility for
   * at least one ".nfs..." file in each directory the file ever gets
   * moved or linked to which happens automagically with the new
   * implementation that only depends on the dcache stuff instead of
   * using the inode layer
   *
   * Unfortunately, things are a little more complicated than indicated
   * above. For a cross-directory move, we want to make sure we can get
   * rid of the old inode after the operation.  This means there must be
   * no pending writes (if it's a file), and the use count must be 1.
   * If these conditions are met, we can drop the dentries before doing
   * the rename.
   */
  static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
  		      struct inode *new_dir, struct dentry *new_dentry)
  {
  	struct inode *old_inode = old_dentry->d_inode;
  	struct inode *new_inode = new_dentry->d_inode;
  	struct dentry *dentry = NULL, *rehash = NULL;
  	int error = -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1898
1899
1900
1901
  	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)
  ",
  		 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
  		 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
b7ab39f63   Nick Piggin   fs: dcache scale ...
1902
  		 new_dentry->d_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1903
1904
  
  	/*
28f79a1a6   Miklos Szeredi   nfs: fix comments...
1905
1906
1907
1908
  	 * For non-directories, check whether the target is busy and if so,
  	 * make a copy of the dentry and then do a silly-rename. If the
  	 * silly-rename succeeds, the copied dentry is hashed and becomes
  	 * the new target.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1909
  	 */
27226104e   Miklos Szeredi   nfs: dont unhash ...
1910
1911
1912
1913
1914
1915
1916
1917
1918
  	if (new_inode && !S_ISDIR(new_inode->i_mode)) {
  		/*
  		 * To prevent any new references to the target during the
  		 * rename, we unhash the dentry in advance.
  		 */
  		if (!d_unhashed(new_dentry)) {
  			d_drop(new_dentry);
  			rehash = new_dentry;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1919

b7ab39f63   Nick Piggin   fs: dcache scale ...
1920
  		if (new_dentry->d_count > 2) {
27226104e   Miklos Szeredi   nfs: dont unhash ...
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
  			int err;
  
  			/* copy the target dentry's name */
  			dentry = d_alloc(new_dentry->d_parent,
  					 &new_dentry->d_name);
  			if (!dentry)
  				goto out;
  
  			/* silly-rename the existing target ... */
  			err = nfs_sillyrename(new_dir, new_dentry);
24e93025e   Miklos Szeredi   nfs: clean up sil...
1931
  			if (err)
27226104e   Miklos Szeredi   nfs: dont unhash ...
1932
  				goto out;
24e93025e   Miklos Szeredi   nfs: clean up sil...
1933
1934
  
  			new_dentry = dentry;
56335936d   OGAWA Hirofumi   nfs: fix oops in ...
1935
  			rehash = NULL;
24e93025e   Miklos Szeredi   nfs: clean up sil...
1936
  			new_inode = NULL;
27226104e   Miklos Szeredi   nfs: dont unhash ...
1937
  		}
b1e4adf4e   Trond Myklebust   NFS: Fix the noti...
1938
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1939

cae7a073a   Trond Myklebust   NFSv4: Return del...
1940
  	nfs_inode_return_delegation(old_inode);
b1e4adf4e   Trond Myklebust   NFS: Fix the noti...
1941
  	if (new_inode != NULL)
24174119c   Trond Myklebust   NFSv4: Ensure tha...
1942
  		nfs_inode_return_delegation(new_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1943

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1944
1945
  	error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
  					   new_dir, &new_dentry->d_name);
5ba7cc480   Trond Myklebust   NFS: Fix post-op ...
1946
  	nfs_mark_for_revalidate(old_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1947
1948
1949
1950
  out:
  	if (rehash)
  		d_rehash(rehash);
  	if (!error) {
b1e4adf4e   Trond Myklebust   NFS: Fix the noti...
1951
1952
  		if (new_inode != NULL)
  			nfs_drop_nlink(new_inode);
349457ccf   Mark Fasheh   [PATCH] Allow fil...
1953
  		d_move(old_dentry, new_dentry);
8fb559f87   Chuck Lever   NFS: Eliminate nf...
1954
1955
  		nfs_set_verifier(new_dentry,
  					nfs_save_change_attribute(new_dir));
d45b9d8ba   Trond Myklebust   NFS: Handle -ENOE...
1956
1957
  	} else if (error == -ENOENT)
  		nfs_dentry_handle_enoent(old_dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1958
1959
1960
1961
  
  	/* new dentry created? */
  	if (dentry)
  		dput(dentry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1962
1963
  	return error;
  }
cfcea3e8c   Trond Myklebust   NFS: Add a global...
1964
1965
1966
  static DEFINE_SPINLOCK(nfs_access_lru_lock);
  static LIST_HEAD(nfs_access_lru_list);
  static atomic_long_t nfs_access_nr_entries;
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
1967
1968
1969
1970
  static void nfs_access_free_entry(struct nfs_access_entry *entry)
  {
  	put_rpccred(entry->cred);
  	kfree(entry);
cfcea3e8c   Trond Myklebust   NFS: Add a global...
1971
1972
1973
  	smp_mb__before_atomic_dec();
  	atomic_long_dec(&nfs_access_nr_entries);
  	smp_mb__after_atomic_dec();
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
1974
  }
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
  static void nfs_access_free_list(struct list_head *head)
  {
  	struct nfs_access_entry *cache;
  
  	while (!list_empty(head)) {
  		cache = list_entry(head->next, struct nfs_access_entry, lru);
  		list_del(&cache->lru);
  		nfs_access_free_entry(cache);
  	}
  }
1495f230f   Ying Han   vmscan: change sh...
1985
1986
  int nfs_access_cache_shrinker(struct shrinker *shrink,
  			      struct shrink_control *sc)
979df72e6   Trond Myklebust   NFS: Add an ACCES...
1987
1988
  {
  	LIST_HEAD(head);
aa510da5b   Trond Myklebust   NFS: We must use ...
1989
  	struct nfs_inode *nfsi, *next;
979df72e6   Trond Myklebust   NFS: Add an ACCES...
1990
  	struct nfs_access_entry *cache;
1495f230f   Ying Han   vmscan: change sh...
1991
1992
  	int nr_to_scan = sc->nr_to_scan;
  	gfp_t gfp_mask = sc->gfp_mask;
979df72e6   Trond Myklebust   NFS: Add an ACCES...
1993

61d5eb298   Trond Myklebust   NFS: Don't run nf...
1994
1995
  	if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL)
  		return (nr_to_scan == 0) ? 0 : -1;
9c7e7e233   Trond Myklebust   NFS: Don't call i...
1996

a50f7951a   Trond Myklebust   NFS: Fix an Oops ...
1997
  	spin_lock(&nfs_access_lru_lock);
aa510da5b   Trond Myklebust   NFS: We must use ...
1998
  	list_for_each_entry_safe(nfsi, next, &nfs_access_lru_list, access_cache_inode_lru) {
979df72e6   Trond Myklebust   NFS: Add an ACCES...
1999
2000
2001
2002
  		struct inode *inode;
  
  		if (nr_to_scan-- == 0)
  			break;
9c7e7e233   Trond Myklebust   NFS: Don't call i...
2003
  		inode = &nfsi->vfs_inode;
979df72e6   Trond Myklebust   NFS: Add an ACCES...
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
  		spin_lock(&inode->i_lock);
  		if (list_empty(&nfsi->access_cache_entry_lru))
  			goto remove_lru_entry;
  		cache = list_entry(nfsi->access_cache_entry_lru.next,
  				struct nfs_access_entry, lru);
  		list_move(&cache->lru, &head);
  		rb_erase(&cache->rb_node, &nfsi->access_cache);
  		if (!list_empty(&nfsi->access_cache_entry_lru))
  			list_move_tail(&nfsi->access_cache_inode_lru,
  					&nfs_access_lru_list);
  		else {
  remove_lru_entry:
  			list_del_init(&nfsi->access_cache_inode_lru);
9c7e7e233   Trond Myklebust   NFS: Don't call i...
2017
  			smp_mb__before_clear_bit();
979df72e6   Trond Myklebust   NFS: Add an ACCES...
2018
  			clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);
9c7e7e233   Trond Myklebust   NFS: Don't call i...
2019
  			smp_mb__after_clear_bit();
979df72e6   Trond Myklebust   NFS: Add an ACCES...
2020
  		}
59844a9bd   Trond Myklebust   NFS: Fix a lock i...
2021
  		spin_unlock(&inode->i_lock);
979df72e6   Trond Myklebust   NFS: Add an ACCES...
2022
2023
  	}
  	spin_unlock(&nfs_access_lru_lock);
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2024
  	nfs_access_free_list(&head);
979df72e6   Trond Myklebust   NFS: Add an ACCES...
2025
2026
  	return (atomic_long_read(&nfs_access_nr_entries) / 100) * sysctl_vfs_cache_pressure;
  }
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2027
  static void __nfs_access_zap_cache(struct nfs_inode *nfsi, struct list_head *head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2028
  {
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2029
  	struct rb_root *root_node = &nfsi->access_cache;
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2030
  	struct rb_node *n;
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2031
2032
2033
2034
2035
2036
  	struct nfs_access_entry *entry;
  
  	/* Unhook entries from the cache */
  	while ((n = rb_first(root_node)) != NULL) {
  		entry = rb_entry(n, struct nfs_access_entry, rb_node);
  		rb_erase(n, root_node);
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2037
  		list_move(&entry->lru, head);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2038
2039
  	}
  	nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2040
  }
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2041
  void nfs_access_zap_cache(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2042
  {
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2043
2044
2045
2046
  	LIST_HEAD(head);
  
  	if (test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags) == 0)
  		return;
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2047
  	/* Remove from global LRU init */
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2048
2049
  	spin_lock(&nfs_access_lru_lock);
  	if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags))
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2050
  		list_del_init(&NFS_I(inode)->access_cache_inode_lru);
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2051

1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2052
  	spin_lock(&inode->i_lock);
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2053
2054
2055
2056
  	__nfs_access_zap_cache(NFS_I(inode), &head);
  	spin_unlock(&inode->i_lock);
  	spin_unlock(&nfs_access_lru_lock);
  	nfs_access_free_list(&head);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2057
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2058

1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
  static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred)
  {
  	struct rb_node *n = NFS_I(inode)->access_cache.rb_node;
  	struct nfs_access_entry *entry;
  
  	while (n != NULL) {
  		entry = rb_entry(n, struct nfs_access_entry, rb_node);
  
  		if (cred < entry->cred)
  			n = n->rb_left;
  		else if (cred > entry->cred)
  			n = n->rb_right;
  		else
  			return entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2073
  	}
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2074
2075
  	return NULL;
  }
af22f94ae   Trond Myklebust   NFSv4: Simplify _...
2076
  static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2077
2078
2079
2080
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_access_entry *cache;
  	int err = -ENOENT;
dc59250c6   Chuck Lever   [PATCH] NFS: Intr...
2081
  	spin_lock(&inode->i_lock);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2082
2083
2084
2085
2086
  	if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
  		goto out_zap;
  	cache = nfs_access_search_rbtree(inode, cred);
  	if (cache == NULL)
  		goto out;
b4d2314bb   Trond Myklebust   NFSv4: Don't igno...
2087
  	if (!nfs_have_delegated_attributes(inode) &&
64672d55d   Peter Staubach   optimize attribut...
2088
  	    !time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2089
2090
2091
2092
  		goto out_stale;
  	res->jiffies = cache->jiffies;
  	res->cred = cache->cred;
  	res->mask = cache->mask;
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2093
  	list_move_tail(&cache->lru, &nfsi->access_cache_entry_lru);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2094
2095
2096
2097
2098
2099
  	err = 0;
  out:
  	spin_unlock(&inode->i_lock);
  	return err;
  out_stale:
  	rb_erase(&cache->rb_node, &nfsi->access_cache);
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2100
  	list_del(&cache->lru);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2101
2102
2103
2104
  	spin_unlock(&inode->i_lock);
  	nfs_access_free_entry(cache);
  	return -ENOENT;
  out_zap:
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2105
2106
  	spin_unlock(&inode->i_lock);
  	nfs_access_zap_cache(inode);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2107
2108
2109
2110
2111
  	return -ENOENT;
  }
  
  static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set)
  {
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2112
2113
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct rb_root *root_node = &nfsi->access_cache;
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
  	struct rb_node **p = &root_node->rb_node;
  	struct rb_node *parent = NULL;
  	struct nfs_access_entry *entry;
  
  	spin_lock(&inode->i_lock);
  	while (*p != NULL) {
  		parent = *p;
  		entry = rb_entry(parent, struct nfs_access_entry, rb_node);
  
  		if (set->cred < entry->cred)
  			p = &parent->rb_left;
  		else if (set->cred > entry->cred)
  			p = &parent->rb_right;
  		else
  			goto found;
  	}
  	rb_link_node(&set->rb_node, parent, p);
  	rb_insert_color(&set->rb_node, root_node);
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2132
  	list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);
dc59250c6   Chuck Lever   [PATCH] NFS: Intr...
2133
  	spin_unlock(&inode->i_lock);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2134
2135
2136
  	return;
  found:
  	rb_replace_node(parent, &set->rb_node, root_node);
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2137
2138
  	list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);
  	list_del(&entry->lru);
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2139
2140
2141
  	spin_unlock(&inode->i_lock);
  	nfs_access_free_entry(entry);
  }
af22f94ae   Trond Myklebust   NFSv4: Simplify _...
2142
  static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2143
2144
2145
2146
2147
  {
  	struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);
  	if (cache == NULL)
  		return;
  	RB_CLEAR_NODE(&cache->rb_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2148
  	cache->jiffies = set->jiffies;
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2149
  	cache->cred = get_rpccred(set->cred);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2150
  	cache->mask = set->mask;
1c3c07e9f   Trond Myklebust   NFS: Add a new AC...
2151
2152
  
  	nfs_access_add_rbtree(inode, cache);
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2153
2154
2155
2156
2157
2158
2159
  
  	/* Update accounting */
  	smp_mb__before_atomic_inc();
  	atomic_long_inc(&nfs_access_nr_entries);
  	smp_mb__after_atomic_inc();
  
  	/* Add inode to global LRU list */
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2160
  	if (!test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) {
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2161
  		spin_lock(&nfs_access_lru_lock);
1a81bb8a1   Trond Myklebust   NFS: Clean up nfs...
2162
2163
2164
  		if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags))
  			list_add_tail(&NFS_I(inode)->access_cache_inode_lru,
  					&nfs_access_lru_list);
cfcea3e8c   Trond Myklebust   NFS: Add a global...
2165
2166
  		spin_unlock(&nfs_access_lru_lock);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
  }
  
  static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
  {
  	struct nfs_access_entry cache;
  	int status;
  
  	status = nfs_access_get_cached(inode, cred, &cache);
  	if (status == 0)
  		goto out;
  
  	/* Be clever: ask server to check for all possible rights */
  	cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
  	cache.cred = cred;
  	cache.jiffies = jiffies;
  	status = NFS_PROTO(inode)->access(inode, &cache);
a71ee337b   Suresh Jayaraman   NFS: Handle -ESTA...
2183
2184
2185
2186
2187
2188
  	if (status != 0) {
  		if (status == -ESTALE) {
  			nfs_zap_caches(inode);
  			if (!S_ISDIR(inode->i_mode))
  				set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2189
  		return status;
a71ee337b   Suresh Jayaraman   NFS: Handle -ESTA...
2190
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2191
2192
  	nfs_access_add_cache(inode, &cache);
  out:
e6305c43e   Al Viro   [PATCH] sanitize ...
2193
  	if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2194
2195
2196
  		return 0;
  	return -EACCES;
  }
af22f94ae   Trond Myklebust   NFSv4: Simplify _...
2197
2198
2199
  static int nfs_open_permission_mask(int openflags)
  {
  	int mask = 0;
8a5e929dd   Al Viro   don't translitera...
2200
  	if ((openflags & O_ACCMODE) != O_WRONLY)
af22f94ae   Trond Myklebust   NFSv4: Simplify _...
2201
  		mask |= MAY_READ;
8a5e929dd   Al Viro   don't translitera...
2202
  	if ((openflags & O_ACCMODE) != O_RDONLY)
af22f94ae   Trond Myklebust   NFSv4: Simplify _...
2203
  		mask |= MAY_WRITE;
8a5e929dd   Al Viro   don't translitera...
2204
  	if (openflags & __FMODE_EXEC)
af22f94ae   Trond Myklebust   NFSv4: Simplify _...
2205
2206
2207
2208
2209
2210
2211
2212
  		mask |= MAY_EXEC;
  	return mask;
  }
  
  int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
  {
  	return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
  }
10556cb21   Al Viro   ->permission() sa...
2213
  int nfs_permission(struct inode *inode, int mask)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2214
2215
2216
  {
  	struct rpc_cred *cred;
  	int res = 0;
10556cb21   Al Viro   ->permission() sa...
2217
  	if (mask & MAY_NOT_BLOCK)
b74c79e99   Nick Piggin   fs: provide rcu-w...
2218
  		return -ECHILD;
91d5b4702   Chuck Lever   NFS: add I/O perf...
2219
  	nfs_inc_stats(inode, NFSIOS_VFSACCESS);
e6305c43e   Al Viro   [PATCH] sanitize ...
2220
  	if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2221
2222
  		goto out;
  	/* Is this sys_access() ? */
9cfcac810   Eric Paris   vfs: re-introduce...
2223
  	if (mask & (MAY_ACCESS | MAY_CHDIR))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2224
2225
2226
2227
2228
2229
2230
2231
  		goto force_lookup;
  
  	switch (inode->i_mode & S_IFMT) {
  		case S_IFLNK:
  			goto out;
  		case S_IFREG:
  			/* NFSv4 has atomic_open... */
  			if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)
7ee2cb7f3   Frank Filz   nfs: Fix NFS v4 c...
2232
2233
  					&& (mask & MAY_OPEN)
  					&& !(mask & MAY_EXEC))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
  				goto out;
  			break;
  		case S_IFDIR:
  			/*
  			 * Optimize away all write operations, since the server
  			 * will check permissions when we perform the op.
  			 */
  			if ((mask & MAY_WRITE) && !(mask & MAY_READ))
  				goto out;
  	}
  
  force_lookup:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2246
2247
  	if (!NFS_PROTO(inode)->access)
  		goto out_notsup;
98a8e3239   Trond Myklebust   SUNRPC: Add a hel...
2248
  	cred = rpc_lookup_cred();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2249
2250
2251
2252
2253
  	if (!IS_ERR(cred)) {
  		res = nfs_do_access(inode, cred, mask);
  		put_rpccred(cred);
  	} else
  		res = PTR_ERR(cred);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2254
  out:
f696a3659   Miklos Szeredi   [PATCH] move exec...
2255
2256
  	if (!res && (mask & MAY_EXEC) && !execute_ok(inode))
  		res = -EACCES;
1e7cb3dc1   Chuck Lever   NFS: directory tr...
2257
2258
2259
  	dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d
  ",
  		inode->i_sb->s_id, inode->i_ino, mask, res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2260
2261
2262
2263
  	return res;
  out_notsup:
  	res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
  	if (res == 0)
2830ba7f3   Al Viro   ->permission() sa...
2264
  		res = generic_permission(inode, mask);
1e7cb3dc1   Chuck Lever   NFS: directory tr...
2265
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2266
2267
2268
2269
2270
2271
2272
2273
  }
  
  /*
   * Local variables:
   *  version-control: t
   *  kept-new-versions: 5
   * End:
   */