diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-25 16:48:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-25 16:48:29 -0400 |
commit | 74eb94b218d087798a52c0b4f1379b635287a4b8 (patch) | |
tree | 4e467c3014c2b1169f6f71d88cf5d1598f3ce28e /fs/nfs | |
parent | 7b6181e06841f5ad15c4ff708b967b4db65a64de (diff) | |
parent | 9a84d38031c258a17bb39beed1e500eadee67407 (diff) |
Merge branch 'nfs-for-2.6.37' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'nfs-for-2.6.37' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (67 commits)
SUNRPC: Cleanup duplicate assignment in rpcauth_refreshcred
nfs: fix unchecked value
Ask for time_delta during fsinfo probe
Revalidate caches on lock
SUNRPC: After calling xprt_release(), we must restart from call_reserve
NFSv4: Fix up the 'dircount' hint in encode_readdir
NFSv4: Clean up nfs4_decode_dirent
NFSv4: nfs4_decode_dirent must clear entry->fattr->valid
NFSv4: Fix a regression in decode_getfattr
NFSv4: Fix up decode_attr_filehandle() to handle the case of empty fh pointer
NFS: Ensure we check all allocation return values in new readdir code
NFS: Readdir plus in v4
NFS: introduce generic decode_getattr function
NFS: check xdr_decode for errors
NFS: nfs_readdir_filler catch all errors
NFS: readdir with vmapped pages
NFS: remove page size checking code
NFS: decode_dirent should use an xdr_stream
SUNRPC: Add a helper function xdr_inline_peek
NFS: remove readdir plus limit
...
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/Kconfig | 11 | ||||
-rw-r--r-- | fs/nfs/client.c | 17 | ||||
-rw-r--r-- | fs/nfs/dir.c | 1013 | ||||
-rw-r--r-- | fs/nfs/file.c | 81 | ||||
-rw-r--r-- | fs/nfs/idmap.c | 211 | ||||
-rw-r--r-- | fs/nfs/inode.c | 36 | ||||
-rw-r--r-- | fs/nfs/internal.h | 12 | ||||
-rw-r--r-- | fs/nfs/mount_clnt.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfs2xdr.c | 107 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 62 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 196 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 4 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 279 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 40 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 340 | ||||
-rw-r--r-- | fs/nfs/nfsroot.c | 566 | ||||
-rw-r--r-- | fs/nfs/proc.c | 35 | ||||
-rw-r--r-- | fs/nfs/read.c | 1 | ||||
-rw-r--r-- | fs/nfs/super.c | 72 | ||||
-rw-r--r-- | fs/nfs/sysctl.c | 2 | ||||
-rw-r--r-- | fs/nfs/unlink.c | 259 | ||||
-rw-r--r-- | fs/nfs/write.c | 18 |
22 files changed, 1986 insertions, 1378 deletions
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index b950415d7c43..5c55c26af165 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig | |||
@@ -118,3 +118,14 @@ config NFS_USE_KERNEL_DNS | |||
118 | select DNS_RESOLVER | 118 | select DNS_RESOLVER |
119 | select KEYS | 119 | select KEYS |
120 | default y | 120 | default y |
121 | |||
122 | config NFS_USE_NEW_IDMAPPER | ||
123 | bool "Use the new idmapper upcall routine" | ||
124 | depends on NFS_V4 && KEYS | ||
125 | help | ||
126 | Say Y here if you want NFS to use the new idmapper upcall functions. | ||
127 | You will need /sbin/request-key (usually provided by the keyutils | ||
128 | package). For details, read | ||
129 | <file:Documentation/filesystems/nfs/idmapper.txt>. | ||
130 | |||
131 | If you are unsure, say N. | ||
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index e7340729af89..a882785eba41 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
@@ -635,7 +635,8 @@ static int nfs_create_rpc_client(struct nfs_client *clp, | |||
635 | */ | 635 | */ |
636 | static void nfs_destroy_server(struct nfs_server *server) | 636 | static void nfs_destroy_server(struct nfs_server *server) |
637 | { | 637 | { |
638 | if (!(server->flags & NFS_MOUNT_NONLM)) | 638 | if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) || |
639 | !(server->flags & NFS_MOUNT_LOCAL_FCNTL)) | ||
639 | nlmclnt_done(server->nlm_host); | 640 | nlmclnt_done(server->nlm_host); |
640 | } | 641 | } |
641 | 642 | ||
@@ -657,7 +658,8 @@ static int nfs_start_lockd(struct nfs_server *server) | |||
657 | 658 | ||
658 | if (nlm_init.nfs_version > 3) | 659 | if (nlm_init.nfs_version > 3) |
659 | return 0; | 660 | return 0; |
660 | if (server->flags & NFS_MOUNT_NONLM) | 661 | if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && |
662 | (server->flags & NFS_MOUNT_LOCAL_FCNTL)) | ||
661 | return 0; | 663 | return 0; |
662 | 664 | ||
663 | switch (clp->cl_proto) { | 665 | switch (clp->cl_proto) { |
@@ -901,8 +903,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo * | |||
901 | server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); | 903 | server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); |
902 | 904 | ||
903 | server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); | 905 | server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); |
904 | if (server->dtsize > PAGE_CACHE_SIZE) | 906 | if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES) |
905 | server->dtsize = PAGE_CACHE_SIZE; | 907 | server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES; |
906 | if (server->dtsize > server->rsize) | 908 | if (server->dtsize > server->rsize) |
907 | server->dtsize = server->rsize; | 909 | server->dtsize = server->rsize; |
908 | 910 | ||
@@ -913,6 +915,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo * | |||
913 | 915 | ||
914 | server->maxfilesize = fsinfo->maxfilesize; | 916 | server->maxfilesize = fsinfo->maxfilesize; |
915 | 917 | ||
918 | server->time_delta = fsinfo->time_delta; | ||
919 | |||
916 | /* We're airborne Set socket buffersize */ | 920 | /* We're airborne Set socket buffersize */ |
917 | rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); | 921 | rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); |
918 | } | 922 | } |
@@ -1356,8 +1360,9 @@ static int nfs4_init_server(struct nfs_server *server, | |||
1356 | 1360 | ||
1357 | /* Initialise the client representation from the mount data */ | 1361 | /* Initialise the client representation from the mount data */ |
1358 | server->flags = data->flags; | 1362 | server->flags = data->flags; |
1359 | server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR| | 1363 | server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK; |
1360 | NFS_CAP_POSIX_LOCK; | 1364 | if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) |
1365 | server->caps |= NFS_CAP_READDIRPLUS; | ||
1361 | server->options = data->options; | 1366 | server->options = data->options; |
1362 | 1367 | ||
1363 | /* Get a client record */ | 1368 | /* Get a client record */ |
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e257172d438c..257e4052492e 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -33,11 +33,12 @@ | |||
33 | #include <linux/namei.h> | 33 | #include <linux/namei.h> |
34 | #include <linux/mount.h> | 34 | #include <linux/mount.h> |
35 | #include <linux/sched.h> | 35 | #include <linux/sched.h> |
36 | #include <linux/vmalloc.h> | ||
36 | 37 | ||
37 | #include "nfs4_fs.h" | ||
38 | #include "delegation.h" | 38 | #include "delegation.h" |
39 | #include "iostat.h" | 39 | #include "iostat.h" |
40 | #include "internal.h" | 40 | #include "internal.h" |
41 | #include "fscache.h" | ||
41 | 42 | ||
42 | /* #define NFS_DEBUG_VERBOSE 1 */ | 43 | /* #define NFS_DEBUG_VERBOSE 1 */ |
43 | 44 | ||
@@ -55,6 +56,7 @@ static int nfs_rename(struct inode *, struct dentry *, | |||
55 | struct inode *, struct dentry *); | 56 | struct inode *, struct dentry *); |
56 | static int nfs_fsync_dir(struct file *, int); | 57 | static int nfs_fsync_dir(struct file *, int); |
57 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); | 58 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); |
59 | static int nfs_readdir_clear_array(struct page*, gfp_t); | ||
58 | 60 | ||
59 | const struct file_operations nfs_dir_operations = { | 61 | const struct file_operations nfs_dir_operations = { |
60 | .llseek = nfs_llseek_dir, | 62 | .llseek = nfs_llseek_dir, |
@@ -80,6 +82,10 @@ const struct inode_operations nfs_dir_inode_operations = { | |||
80 | .setattr = nfs_setattr, | 82 | .setattr = nfs_setattr, |
81 | }; | 83 | }; |
82 | 84 | ||
85 | const struct address_space_operations nfs_dir_addr_space_ops = { | ||
86 | .releasepage = nfs_readdir_clear_array, | ||
87 | }; | ||
88 | |||
83 | #ifdef CONFIG_NFS_V3 | 89 | #ifdef CONFIG_NFS_V3 |
84 | const struct inode_operations nfs3_dir_inode_operations = { | 90 | const struct inode_operations nfs3_dir_inode_operations = { |
85 | .create = nfs_create, | 91 | .create = nfs_create, |
@@ -104,8 +110,9 @@ const struct inode_operations nfs3_dir_inode_operations = { | |||
104 | #ifdef CONFIG_NFS_V4 | 110 | #ifdef CONFIG_NFS_V4 |
105 | 111 | ||
106 | static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); | 112 | static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); |
113 | static int nfs_open_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd); | ||
107 | const struct inode_operations nfs4_dir_inode_operations = { | 114 | const struct inode_operations nfs4_dir_inode_operations = { |
108 | .create = nfs_create, | 115 | .create = nfs_open_create, |
109 | .lookup = nfs_atomic_lookup, | 116 | .lookup = nfs_atomic_lookup, |
110 | .link = nfs_link, | 117 | .link = nfs_link, |
111 | .unlink = nfs_unlink, | 118 | .unlink = nfs_unlink, |
@@ -150,51 +157,197 @@ nfs_opendir(struct inode *inode, struct file *filp) | |||
150 | return res; | 157 | return res; |
151 | } | 158 | } |
152 | 159 | ||
153 | typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int); | 160 | struct nfs_cache_array_entry { |
161 | u64 cookie; | ||
162 | u64 ino; | ||
163 | struct qstr string; | ||
164 | }; | ||
165 | |||
166 | struct nfs_cache_array { | ||
167 | unsigned int size; | ||
168 | int eof_index; | ||
169 | u64 last_cookie; | ||
170 | struct nfs_cache_array_entry array[0]; | ||
171 | }; | ||
172 | |||
173 | #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) | ||
174 | |||
175 | typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); | ||
154 | typedef struct { | 176 | typedef struct { |
155 | struct file *file; | 177 | struct file *file; |
156 | struct page *page; | 178 | struct page *page; |
157 | unsigned long page_index; | 179 | unsigned long page_index; |
158 | __be32 *ptr; | ||
159 | u64 *dir_cookie; | 180 | u64 *dir_cookie; |
160 | loff_t current_index; | 181 | loff_t current_index; |
161 | struct nfs_entry *entry; | ||
162 | decode_dirent_t decode; | 182 | decode_dirent_t decode; |
163 | int plus; | 183 | |
164 | unsigned long timestamp; | 184 | unsigned long timestamp; |
165 | unsigned long gencount; | 185 | unsigned long gencount; |
166 | int timestamp_valid; | 186 | unsigned int cache_entry_index; |
187 | unsigned int plus:1; | ||
188 | unsigned int eof:1; | ||
167 | } nfs_readdir_descriptor_t; | 189 | } nfs_readdir_descriptor_t; |
168 | 190 | ||
169 | /* Now we cache directories properly, by stuffing the dirent | 191 | /* |
170 | * data directly in the page cache. | 192 | * The caller is responsible for calling nfs_readdir_release_array(page) |
171 | * | ||
172 | * Inode invalidation due to refresh etc. takes care of | ||
173 | * _everything_, no sloppy entry flushing logic, no extraneous | ||
174 | * copying, network direct to page cache, the way it was meant | ||
175 | * to be. | ||
176 | * | ||
177 | * NOTE: Dirent information verification is done always by the | ||
178 | * page-in of the RPC reply, nowhere else, this simplies | ||
179 | * things substantially. | ||
180 | */ | 193 | */ |
181 | static | 194 | static |
182 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) | 195 | struct nfs_cache_array *nfs_readdir_get_array(struct page *page) |
196 | { | ||
197 | if (page == NULL) | ||
198 | return ERR_PTR(-EIO); | ||
199 | return (struct nfs_cache_array *)kmap(page); | ||
200 | } | ||
201 | |||
202 | static | ||
203 | void nfs_readdir_release_array(struct page *page) | ||
204 | { | ||
205 | kunmap(page); | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * we are freeing strings created by nfs_add_to_readdir_array() | ||
210 | */ | ||
211 | static | ||
212 | int nfs_readdir_clear_array(struct page *page, gfp_t mask) | ||
213 | { | ||
214 | struct nfs_cache_array *array = nfs_readdir_get_array(page); | ||
215 | int i; | ||
216 | for (i = 0; i < array->size; i++) | ||
217 | kfree(array->array[i].string.name); | ||
218 | nfs_readdir_release_array(page); | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * the caller is responsible for freeing qstr.name | ||
224 | * when called by nfs_readdir_add_to_array, the strings will be freed in | ||
225 | * nfs_clear_readdir_array() | ||
226 | */ | ||
227 | static | ||
228 | int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len) | ||
229 | { | ||
230 | string->len = len; | ||
231 | string->name = kmemdup(name, len, GFP_KERNEL); | ||
232 | if (string->name == NULL) | ||
233 | return -ENOMEM; | ||
234 | string->hash = full_name_hash(name, len); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static | ||
239 | int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page) | ||
240 | { | ||
241 | struct nfs_cache_array *array = nfs_readdir_get_array(page); | ||
242 | struct nfs_cache_array_entry *cache_entry; | ||
243 | int ret; | ||
244 | |||
245 | if (IS_ERR(array)) | ||
246 | return PTR_ERR(array); | ||
247 | ret = -EIO; | ||
248 | if (array->size >= MAX_READDIR_ARRAY) | ||
249 | goto out; | ||
250 | |||
251 | cache_entry = &array->array[array->size]; | ||
252 | cache_entry->cookie = entry->prev_cookie; | ||
253 | cache_entry->ino = entry->ino; | ||
254 | ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len); | ||
255 | if (ret) | ||
256 | goto out; | ||
257 | array->last_cookie = entry->cookie; | ||
258 | if (entry->eof == 1) | ||
259 | array->eof_index = array->size; | ||
260 | array->size++; | ||
261 | out: | ||
262 | nfs_readdir_release_array(page); | ||
263 | return ret; | ||
264 | } | ||
265 | |||
266 | static | ||
267 | int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) | ||
268 | { | ||
269 | loff_t diff = desc->file->f_pos - desc->current_index; | ||
270 | unsigned int index; | ||
271 | |||
272 | if (diff < 0) | ||
273 | goto out_eof; | ||
274 | if (diff >= array->size) { | ||
275 | if (array->eof_index > 0) | ||
276 | goto out_eof; | ||
277 | desc->current_index += array->size; | ||
278 | return -EAGAIN; | ||
279 | } | ||
280 | |||
281 | index = (unsigned int)diff; | ||
282 | *desc->dir_cookie = array->array[index].cookie; | ||
283 | desc->cache_entry_index = index; | ||
284 | if (index == array->eof_index) | ||
285 | desc->eof = 1; | ||
286 | return 0; | ||
287 | out_eof: | ||
288 | desc->eof = 1; | ||
289 | return -EBADCOOKIE; | ||
290 | } | ||
291 | |||
292 | static | ||
293 | int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) | ||
294 | { | ||
295 | int i; | ||
296 | int status = -EAGAIN; | ||
297 | |||
298 | for (i = 0; i < array->size; i++) { | ||
299 | if (i == array->eof_index) { | ||
300 | desc->eof = 1; | ||
301 | status = -EBADCOOKIE; | ||
302 | } | ||
303 | if (array->array[i].cookie == *desc->dir_cookie) { | ||
304 | desc->cache_entry_index = i; | ||
305 | status = 0; | ||
306 | break; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | return status; | ||
311 | } | ||
312 | |||
313 | static | ||
314 | int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc) | ||
315 | { | ||
316 | struct nfs_cache_array *array; | ||
317 | int status = -EBADCOOKIE; | ||
318 | |||
319 | if (desc->dir_cookie == NULL) | ||
320 | goto out; | ||
321 | |||
322 | array = nfs_readdir_get_array(desc->page); | ||
323 | if (IS_ERR(array)) { | ||
324 | status = PTR_ERR(array); | ||
325 | goto out; | ||
326 | } | ||
327 | |||
328 | if (*desc->dir_cookie == 0) | ||
329 | status = nfs_readdir_search_for_pos(array, desc); | ||
330 | else | ||
331 | status = nfs_readdir_search_for_cookie(array, desc); | ||
332 | |||
333 | nfs_readdir_release_array(desc->page); | ||
334 | out: | ||
335 | return status; | ||
336 | } | ||
337 | |||
338 | /* Fill a page with xdr information before transferring to the cache page */ | ||
339 | static | ||
340 | int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, | ||
341 | struct nfs_entry *entry, struct file *file, struct inode *inode) | ||
183 | { | 342 | { |
184 | struct file *file = desc->file; | ||
185 | struct inode *inode = file->f_path.dentry->d_inode; | ||
186 | struct rpc_cred *cred = nfs_file_cred(file); | 343 | struct rpc_cred *cred = nfs_file_cred(file); |
187 | unsigned long timestamp, gencount; | 344 | unsigned long timestamp, gencount; |
188 | int error; | 345 | int error; |
189 | 346 | ||
190 | dfprintk(DIRCACHE, "NFS: %s: reading cookie %Lu into page %lu\n", | ||
191 | __func__, (long long)desc->entry->cookie, | ||
192 | page->index); | ||
193 | |||
194 | again: | 347 | again: |
195 | timestamp = jiffies; | 348 | timestamp = jiffies; |
196 | gencount = nfs_inc_attr_generation_counter(); | 349 | gencount = nfs_inc_attr_generation_counter(); |
197 | error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, desc->entry->cookie, page, | 350 | error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages, |
198 | NFS_SERVER(inode)->dtsize, desc->plus); | 351 | NFS_SERVER(inode)->dtsize, desc->plus); |
199 | if (error < 0) { | 352 | if (error < 0) { |
200 | /* We requested READDIRPLUS, but the server doesn't grok it */ | 353 | /* We requested READDIRPLUS, but the server doesn't grok it */ |
@@ -208,190 +361,292 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) | |||
208 | } | 361 | } |
209 | desc->timestamp = timestamp; | 362 | desc->timestamp = timestamp; |
210 | desc->gencount = gencount; | 363 | desc->gencount = gencount; |
211 | desc->timestamp_valid = 1; | 364 | error: |
212 | SetPageUptodate(page); | 365 | return error; |
213 | /* Ensure consistent page alignment of the data. | ||
214 | * Note: assumes we have exclusive access to this mapping either | ||
215 | * through inode->i_mutex or some other mechanism. | ||
216 | */ | ||
217 | if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) { | ||
218 | /* Should never happen */ | ||
219 | nfs_zap_mapping(inode, inode->i_mapping); | ||
220 | } | ||
221 | unlock_page(page); | ||
222 | return 0; | ||
223 | error: | ||
224 | unlock_page(page); | ||
225 | return -EIO; | ||
226 | } | 366 | } |
227 | 367 | ||
228 | static inline | 368 | /* Fill in an entry based on the xdr code stored in desc->page */ |
229 | int dir_decode(nfs_readdir_descriptor_t *desc) | 369 | static |
370 | int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream) | ||
230 | { | 371 | { |
231 | __be32 *p = desc->ptr; | 372 | __be32 *p = desc->decode(stream, entry, NFS_SERVER(desc->file->f_path.dentry->d_inode), desc->plus); |
232 | p = desc->decode(p, desc->entry, desc->plus); | ||
233 | if (IS_ERR(p)) | 373 | if (IS_ERR(p)) |
234 | return PTR_ERR(p); | 374 | return PTR_ERR(p); |
235 | desc->ptr = p; | 375 | |
236 | if (desc->timestamp_valid) { | 376 | entry->fattr->time_start = desc->timestamp; |
237 | desc->entry->fattr->time_start = desc->timestamp; | 377 | entry->fattr->gencount = desc->gencount; |
238 | desc->entry->fattr->gencount = desc->gencount; | ||
239 | } else | ||
240 | desc->entry->fattr->valid &= ~NFS_ATTR_FATTR; | ||
241 | return 0; | 378 | return 0; |
242 | } | 379 | } |
243 | 380 | ||
244 | static inline | 381 | static |
245 | void dir_page_release(nfs_readdir_descriptor_t *desc) | 382 | int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry) |
246 | { | 383 | { |
247 | kunmap(desc->page); | 384 | struct nfs_inode *node; |
248 | page_cache_release(desc->page); | 385 | if (dentry->d_inode == NULL) |
249 | desc->page = NULL; | 386 | goto different; |
250 | desc->ptr = NULL; | 387 | node = NFS_I(dentry->d_inode); |
388 | if (node->fh.size != entry->fh->size) | ||
389 | goto different; | ||
390 | if (strncmp(node->fh.data, entry->fh->data, node->fh.size) != 0) | ||
391 | goto different; | ||
392 | return 1; | ||
393 | different: | ||
394 | return 0; | ||
251 | } | 395 | } |
252 | 396 | ||
253 | /* | 397 | static |
254 | * Given a pointer to a buffer that has already been filled by a call | 398 | void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) |
255 | * to readdir, find the next entry with cookie '*desc->dir_cookie'. | ||
256 | * | ||
257 | * If the end of the buffer has been reached, return -EAGAIN, if not, | ||
258 | * return the offset within the buffer of the next entry to be | ||
259 | * read. | ||
260 | */ | ||
261 | static inline | ||
262 | int find_dirent(nfs_readdir_descriptor_t *desc) | ||
263 | { | 399 | { |
264 | struct nfs_entry *entry = desc->entry; | 400 | struct qstr filename = { |
265 | int loop_count = 0, | 401 | .len = entry->len, |
266 | status; | 402 | .name = entry->name, |
403 | }; | ||
404 | struct dentry *dentry; | ||
405 | struct dentry *alias; | ||
406 | struct inode *dir = parent->d_inode; | ||
407 | struct inode *inode; | ||
267 | 408 | ||
268 | while((status = dir_decode(desc)) == 0) { | 409 | if (filename.name[0] == '.') { |
269 | dfprintk(DIRCACHE, "NFS: %s: examining cookie %Lu\n", | 410 | if (filename.len == 1) |
270 | __func__, (unsigned long long)entry->cookie); | 411 | return; |
271 | if (entry->prev_cookie == *desc->dir_cookie) | 412 | if (filename.len == 2 && filename.name[1] == '.') |
272 | break; | 413 | return; |
273 | if (loop_count++ > 200) { | 414 | } |
274 | loop_count = 0; | 415 | filename.hash = full_name_hash(filename.name, filename.len); |
275 | schedule(); | 416 | |
417 | dentry = d_lookup(parent, &filename); | ||
418 | if (dentry != NULL) { | ||
419 | if (nfs_same_file(dentry, entry)) { | ||
420 | nfs_refresh_inode(dentry->d_inode, entry->fattr); | ||
421 | goto out; | ||
422 | } else { | ||
423 | d_drop(dentry); | ||
424 | dput(dentry); | ||
276 | } | 425 | } |
277 | } | 426 | } |
278 | return status; | 427 | |
428 | dentry = d_alloc(parent, &filename); | ||
429 | if (dentry == NULL) | ||
430 | return; | ||
431 | |||
432 | dentry->d_op = NFS_PROTO(dir)->dentry_ops; | ||
433 | inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr); | ||
434 | if (IS_ERR(inode)) | ||
435 | goto out; | ||
436 | |||
437 | alias = d_materialise_unique(dentry, inode); | ||
438 | if (IS_ERR(alias)) | ||
439 | goto out; | ||
440 | else if (alias) { | ||
441 | nfs_set_verifier(alias, nfs_save_change_attribute(dir)); | ||
442 | dput(alias); | ||
443 | } else | ||
444 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
445 | |||
446 | out: | ||
447 | dput(dentry); | ||
448 | } | ||
449 | |||
450 | /* Perform conversion from xdr to cache array */ | ||
451 | static | ||
452 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, | ||
453 | void *xdr_page, struct page *page, unsigned int buflen) | ||
454 | { | ||
455 | struct xdr_stream stream; | ||
456 | struct xdr_buf buf; | ||
457 | __be32 *ptr = xdr_page; | ||
458 | int status; | ||
459 | struct nfs_cache_array *array; | ||
460 | |||
461 | buf.head->iov_base = xdr_page; | ||
462 | buf.head->iov_len = buflen; | ||
463 | buf.tail->iov_len = 0; | ||
464 | buf.page_base = 0; | ||
465 | buf.page_len = 0; | ||
466 | buf.buflen = buf.head->iov_len; | ||
467 | buf.len = buf.head->iov_len; | ||
468 | |||
469 | xdr_init_decode(&stream, &buf, ptr); | ||
470 | |||
471 | |||
472 | do { | ||
473 | status = xdr_decode(desc, entry, &stream); | ||
474 | if (status != 0) | ||
475 | break; | ||
476 | |||
477 | if (nfs_readdir_add_to_array(entry, page) == -1) | ||
478 | break; | ||
479 | if (desc->plus == 1) | ||
480 | nfs_prime_dcache(desc->file->f_path.dentry, entry); | ||
481 | } while (!entry->eof); | ||
482 | |||
483 | if (status == -EBADCOOKIE && entry->eof) { | ||
484 | array = nfs_readdir_get_array(page); | ||
485 | array->eof_index = array->size - 1; | ||
486 | status = 0; | ||
487 | nfs_readdir_release_array(page); | ||
488 | } | ||
489 | } | ||
490 | |||
491 | static | ||
492 | void nfs_readdir_free_pagearray(struct page **pages, unsigned int npages) | ||
493 | { | ||
494 | unsigned int i; | ||
495 | for (i = 0; i < npages; i++) | ||
496 | put_page(pages[i]); | ||
497 | } | ||
498 | |||
499 | static | ||
500 | void nfs_readdir_free_large_page(void *ptr, struct page **pages, | ||
501 | unsigned int npages) | ||
502 | { | ||
503 | vm_unmap_ram(ptr, npages); | ||
504 | nfs_readdir_free_pagearray(pages, npages); | ||
279 | } | 505 | } |
280 | 506 | ||
281 | /* | 507 | /* |
282 | * Given a pointer to a buffer that has already been filled by a call | 508 | * nfs_readdir_large_page will allocate pages that must be freed with a call |
283 | * to readdir, find the entry at offset 'desc->file->f_pos'. | 509 | * to nfs_readdir_free_large_page |
284 | * | ||
285 | * If the end of the buffer has been reached, return -EAGAIN, if not, | ||
286 | * return the offset within the buffer of the next entry to be | ||
287 | * read. | ||
288 | */ | 510 | */ |
289 | static inline | 511 | static |
290 | int find_dirent_index(nfs_readdir_descriptor_t *desc) | 512 | void *nfs_readdir_large_page(struct page **pages, unsigned int npages) |
291 | { | 513 | { |
292 | struct nfs_entry *entry = desc->entry; | 514 | void *ptr; |
293 | int loop_count = 0, | 515 | unsigned int i; |
294 | status; | 516 | |
517 | for (i = 0; i < npages; i++) { | ||
518 | struct page *page = alloc_page(GFP_KERNEL); | ||
519 | if (page == NULL) | ||
520 | goto out_freepages; | ||
521 | pages[i] = page; | ||
522 | } | ||
295 | 523 | ||
296 | for(;;) { | 524 | ptr = vm_map_ram(pages, npages, 0, PAGE_KERNEL); |
297 | status = dir_decode(desc); | 525 | if (!IS_ERR_OR_NULL(ptr)) |
298 | if (status) | 526 | return ptr; |
299 | break; | 527 | out_freepages: |
528 | nfs_readdir_free_pagearray(pages, i); | ||
529 | return NULL; | ||
530 | } | ||
531 | |||
532 | static | ||
533 | int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode) | ||
534 | { | ||
535 | struct page *pages[NFS_MAX_READDIR_PAGES]; | ||
536 | void *pages_ptr = NULL; | ||
537 | struct nfs_entry entry; | ||
538 | struct file *file = desc->file; | ||
539 | struct nfs_cache_array *array; | ||
540 | int status = 0; | ||
541 | unsigned int array_size = ARRAY_SIZE(pages); | ||
542 | |||
543 | entry.prev_cookie = 0; | ||
544 | entry.cookie = *desc->dir_cookie; | ||
545 | entry.eof = 0; | ||
546 | entry.fh = nfs_alloc_fhandle(); | ||
547 | entry.fattr = nfs_alloc_fattr(); | ||
548 | if (entry.fh == NULL || entry.fattr == NULL) | ||
549 | goto out; | ||
300 | 550 | ||
301 | dfprintk(DIRCACHE, "NFS: found cookie %Lu at index %Ld\n", | 551 | array = nfs_readdir_get_array(page); |
302 | (unsigned long long)entry->cookie, desc->current_index); | 552 | memset(array, 0, sizeof(struct nfs_cache_array)); |
553 | array->eof_index = -1; | ||
303 | 554 | ||
304 | if (desc->file->f_pos == desc->current_index) { | 555 | pages_ptr = nfs_readdir_large_page(pages, array_size); |
305 | *desc->dir_cookie = entry->cookie; | 556 | if (!pages_ptr) |
557 | goto out_release_array; | ||
558 | do { | ||
559 | status = nfs_readdir_xdr_filler(pages, desc, &entry, file, inode); | ||
560 | |||
561 | if (status < 0) | ||
306 | break; | 562 | break; |
307 | } | 563 | nfs_readdir_page_filler(desc, &entry, pages_ptr, page, array_size * PAGE_SIZE); |
308 | desc->current_index++; | 564 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); |
309 | if (loop_count++ > 200) { | 565 | |
310 | loop_count = 0; | 566 | nfs_readdir_free_large_page(pages_ptr, pages, array_size); |
311 | schedule(); | 567 | out_release_array: |
312 | } | 568 | nfs_readdir_release_array(page); |
313 | } | 569 | out: |
570 | nfs_free_fattr(entry.fattr); | ||
571 | nfs_free_fhandle(entry.fh); | ||
314 | return status; | 572 | return status; |
315 | } | 573 | } |
316 | 574 | ||
317 | /* | 575 | /* |
318 | * Find the given page, and call find_dirent() or find_dirent_index in | 576 | * Now we cache directories properly, by converting xdr information |
319 | * order to try to return the next entry. | 577 | * to an array that can be used for lookups later. This results in |
578 | * fewer cache pages, since we can store more information on each page. | ||
579 | * We only need to convert from xdr once so future lookups are much simpler | ||
320 | */ | 580 | */ |
321 | static inline | 581 | static |
322 | int find_dirent_page(nfs_readdir_descriptor_t *desc) | 582 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) |
323 | { | 583 | { |
324 | struct inode *inode = desc->file->f_path.dentry->d_inode; | 584 | struct inode *inode = desc->file->f_path.dentry->d_inode; |
325 | struct page *page; | ||
326 | int status; | ||
327 | 585 | ||
328 | dfprintk(DIRCACHE, "NFS: %s: searching page %ld for target %Lu\n", | 586 | if (nfs_readdir_xdr_to_array(desc, page, inode) < 0) |
329 | __func__, desc->page_index, | 587 | goto error; |
330 | (long long) *desc->dir_cookie); | 588 | SetPageUptodate(page); |
331 | 589 | ||
332 | /* If we find the page in the page_cache, we cannot be sure | 590 | if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) { |
333 | * how fresh the data is, so we will ignore readdir_plus attributes. | 591 | /* Should never happen */ |
334 | */ | 592 | nfs_zap_mapping(inode, inode->i_mapping); |
335 | desc->timestamp_valid = 0; | ||
336 | page = read_cache_page(inode->i_mapping, desc->page_index, | ||
337 | (filler_t *)nfs_readdir_filler, desc); | ||
338 | if (IS_ERR(page)) { | ||
339 | status = PTR_ERR(page); | ||
340 | goto out; | ||
341 | } | 593 | } |
594 | unlock_page(page); | ||
595 | return 0; | ||
596 | error: | ||
597 | unlock_page(page); | ||
598 | return -EIO; | ||
599 | } | ||
342 | 600 | ||
343 | /* NOTE: Someone else may have changed the READDIRPLUS flag */ | 601 | static |
344 | desc->page = page; | 602 | void cache_page_release(nfs_readdir_descriptor_t *desc) |
345 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | 603 | { |
346 | if (*desc->dir_cookie != 0) | 604 | page_cache_release(desc->page); |
347 | status = find_dirent(desc); | 605 | desc->page = NULL; |
348 | else | 606 | } |
349 | status = find_dirent_index(desc); | 607 | |
350 | if (status < 0) | 608 | static |
351 | dir_page_release(desc); | 609 | struct page *get_cache_page(nfs_readdir_descriptor_t *desc) |
352 | out: | 610 | { |
353 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, status); | 611 | struct page *page; |
354 | return status; | 612 | page = read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping, |
613 | desc->page_index, (filler_t *)nfs_readdir_filler, desc); | ||
614 | if (IS_ERR(page)) | ||
615 | desc->eof = 1; | ||
616 | return page; | ||
355 | } | 617 | } |
356 | 618 | ||
357 | /* | 619 | /* |
358 | * Recurse through the page cache pages, and return a | 620 | * Returns 0 if desc->dir_cookie was found on page desc->page_index |
359 | * filled nfs_entry structure of the next directory entry if possible. | ||
360 | * | ||
361 | * The target for the search is '*desc->dir_cookie' if non-0, | ||
362 | * 'desc->file->f_pos' otherwise | ||
363 | */ | 621 | */ |
622 | static | ||
623 | int find_cache_page(nfs_readdir_descriptor_t *desc) | ||
624 | { | ||
625 | int res; | ||
626 | |||
627 | desc->page = get_cache_page(desc); | ||
628 | if (IS_ERR(desc->page)) | ||
629 | return PTR_ERR(desc->page); | ||
630 | |||
631 | res = nfs_readdir_search_array(desc); | ||
632 | if (res == 0) | ||
633 | return 0; | ||
634 | cache_page_release(desc); | ||
635 | return res; | ||
636 | } | ||
637 | |||
638 | /* Search for desc->dir_cookie from the beginning of the page cache */ | ||
364 | static inline | 639 | static inline |
365 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | 640 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) |
366 | { | 641 | { |
367 | int loop_count = 0; | 642 | int res = -EAGAIN; |
368 | int res; | ||
369 | |||
370 | /* Always search-by-index from the beginning of the cache */ | ||
371 | if (*desc->dir_cookie == 0) { | ||
372 | dfprintk(DIRCACHE, "NFS: readdir_search_pagecache() searching for offset %Ld\n", | ||
373 | (long long)desc->file->f_pos); | ||
374 | desc->page_index = 0; | ||
375 | desc->entry->cookie = desc->entry->prev_cookie = 0; | ||
376 | desc->entry->eof = 0; | ||
377 | desc->current_index = 0; | ||
378 | } else | ||
379 | dfprintk(DIRCACHE, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", | ||
380 | (unsigned long long)*desc->dir_cookie); | ||
381 | 643 | ||
382 | for (;;) { | 644 | while (1) { |
383 | res = find_dirent_page(desc); | 645 | res = find_cache_page(desc); |
384 | if (res != -EAGAIN) | 646 | if (res != -EAGAIN) |
385 | break; | 647 | break; |
386 | /* Align to beginning of next page */ | 648 | desc->page_index++; |
387 | desc->page_index ++; | ||
388 | if (loop_count++ > 200) { | ||
389 | loop_count = 0; | ||
390 | schedule(); | ||
391 | } | ||
392 | } | 649 | } |
393 | |||
394 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, res); | ||
395 | return res; | 650 | return res; |
396 | } | 651 | } |
397 | 652 | ||
@@ -400,8 +655,6 @@ static inline unsigned int dt_type(struct inode *inode) | |||
400 | return (inode->i_mode >> 12) & 15; | 655 | return (inode->i_mode >> 12) & 15; |
401 | } | 656 | } |
402 | 657 | ||
403 | static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc); | ||
404 | |||
405 | /* | 658 | /* |
406 | * Once we've found the start of the dirent within a page: fill 'er up... | 659 | * Once we've found the start of the dirent within a page: fill 'er up... |
407 | */ | 660 | */ |
@@ -410,49 +663,36 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
410 | filldir_t filldir) | 663 | filldir_t filldir) |
411 | { | 664 | { |
412 | struct file *file = desc->file; | 665 | struct file *file = desc->file; |
413 | struct nfs_entry *entry = desc->entry; | 666 | int i = 0; |
414 | struct dentry *dentry = NULL; | 667 | int res = 0; |
415 | u64 fileid; | 668 | struct nfs_cache_array *array = NULL; |
416 | int loop_count = 0, | 669 | unsigned int d_type = DT_UNKNOWN; |
417 | res; | 670 | struct dentry *dentry = NULL; |
418 | |||
419 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", | ||
420 | (unsigned long long)entry->cookie); | ||
421 | |||
422 | for(;;) { | ||
423 | unsigned d_type = DT_UNKNOWN; | ||
424 | /* Note: entry->prev_cookie contains the cookie for | ||
425 | * retrieving the current dirent on the server */ | ||
426 | fileid = entry->ino; | ||
427 | |||
428 | /* Get a dentry if we have one */ | ||
429 | if (dentry != NULL) | ||
430 | dput(dentry); | ||
431 | dentry = nfs_readdir_lookup(desc); | ||
432 | 671 | ||
433 | /* Use readdirplus info */ | 672 | array = nfs_readdir_get_array(desc->page); |
434 | if (dentry != NULL && dentry->d_inode != NULL) { | ||
435 | d_type = dt_type(dentry->d_inode); | ||
436 | fileid = NFS_FILEID(dentry->d_inode); | ||
437 | } | ||
438 | 673 | ||
439 | res = filldir(dirent, entry->name, entry->len, | 674 | for (i = desc->cache_entry_index; i < array->size; i++) { |
440 | file->f_pos, nfs_compat_user_ino64(fileid), | 675 | d_type = DT_UNKNOWN; |
441 | d_type); | 676 | |
677 | res = filldir(dirent, array->array[i].string.name, | ||
678 | array->array[i].string.len, file->f_pos, | ||
679 | nfs_compat_user_ino64(array->array[i].ino), d_type); | ||
442 | if (res < 0) | 680 | if (res < 0) |
443 | break; | 681 | break; |
444 | file->f_pos++; | 682 | file->f_pos++; |
445 | *desc->dir_cookie = entry->cookie; | 683 | desc->cache_entry_index = i; |
446 | if (dir_decode(desc) != 0) { | 684 | if (i < (array->size-1)) |
447 | desc->page_index ++; | 685 | *desc->dir_cookie = array->array[i+1].cookie; |
686 | else | ||
687 | *desc->dir_cookie = array->last_cookie; | ||
688 | if (i == array->eof_index) { | ||
689 | desc->eof = 1; | ||
448 | break; | 690 | break; |
449 | } | 691 | } |
450 | if (loop_count++ > 200) { | ||
451 | loop_count = 0; | ||
452 | schedule(); | ||
453 | } | ||
454 | } | 692 | } |
455 | dir_page_release(desc); | 693 | |
694 | nfs_readdir_release_array(desc->page); | ||
695 | cache_page_release(desc); | ||
456 | if (dentry != NULL) | 696 | if (dentry != NULL) |
457 | dput(dentry); | 697 | dput(dentry); |
458 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", | 698 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", |
@@ -476,12 +716,9 @@ static inline | |||
476 | int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | 716 | int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, |
477 | filldir_t filldir) | 717 | filldir_t filldir) |
478 | { | 718 | { |
479 | struct file *file = desc->file; | ||
480 | struct inode *inode = file->f_path.dentry->d_inode; | ||
481 | struct rpc_cred *cred = nfs_file_cred(file); | ||
482 | struct page *page = NULL; | 719 | struct page *page = NULL; |
483 | int status; | 720 | int status; |
484 | unsigned long timestamp, gencount; | 721 | struct inode *inode = desc->file->f_path.dentry->d_inode; |
485 | 722 | ||
486 | dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", | 723 | dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", |
487 | (unsigned long long)*desc->dir_cookie); | 724 | (unsigned long long)*desc->dir_cookie); |
@@ -491,38 +728,22 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
491 | status = -ENOMEM; | 728 | status = -ENOMEM; |
492 | goto out; | 729 | goto out; |
493 | } | 730 | } |
494 | timestamp = jiffies; | 731 | |
495 | gencount = nfs_inc_attr_generation_counter(); | 732 | if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) { |
496 | status = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, | ||
497 | *desc->dir_cookie, page, | ||
498 | NFS_SERVER(inode)->dtsize, | ||
499 | desc->plus); | ||
500 | desc->page = page; | ||
501 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | ||
502 | if (status >= 0) { | ||
503 | desc->timestamp = timestamp; | ||
504 | desc->gencount = gencount; | ||
505 | desc->timestamp_valid = 1; | ||
506 | if ((status = dir_decode(desc)) == 0) | ||
507 | desc->entry->prev_cookie = *desc->dir_cookie; | ||
508 | } else | ||
509 | status = -EIO; | 733 | status = -EIO; |
510 | if (status < 0) | ||
511 | goto out_release; | 734 | goto out_release; |
735 | } | ||
512 | 736 | ||
737 | desc->page_index = 0; | ||
738 | desc->page = page; | ||
513 | status = nfs_do_filldir(desc, dirent, filldir); | 739 | status = nfs_do_filldir(desc, dirent, filldir); |
514 | 740 | ||
515 | /* Reset read descriptor so it searches the page cache from | ||
516 | * the start upon the next call to readdir_search_pagecache() */ | ||
517 | desc->page_index = 0; | ||
518 | desc->entry->cookie = desc->entry->prev_cookie = 0; | ||
519 | desc->entry->eof = 0; | ||
520 | out: | 741 | out: |
521 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", | 742 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", |
522 | __func__, status); | 743 | __func__, status); |
523 | return status; | 744 | return status; |
524 | out_release: | 745 | out_release: |
525 | dir_page_release(desc); | 746 | cache_page_release(desc); |
526 | goto out; | 747 | goto out; |
527 | } | 748 | } |
528 | 749 | ||
@@ -536,7 +757,6 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
536 | struct inode *inode = dentry->d_inode; | 757 | struct inode *inode = dentry->d_inode; |
537 | nfs_readdir_descriptor_t my_desc, | 758 | nfs_readdir_descriptor_t my_desc, |
538 | *desc = &my_desc; | 759 | *desc = &my_desc; |
539 | struct nfs_entry my_entry; | ||
540 | int res = -ENOMEM; | 760 | int res = -ENOMEM; |
541 | 761 | ||
542 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", | 762 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", |
@@ -557,26 +777,17 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
557 | desc->decode = NFS_PROTO(inode)->decode_dirent; | 777 | desc->decode = NFS_PROTO(inode)->decode_dirent; |
558 | desc->plus = NFS_USE_READDIRPLUS(inode); | 778 | desc->plus = NFS_USE_READDIRPLUS(inode); |
559 | 779 | ||
560 | my_entry.cookie = my_entry.prev_cookie = 0; | ||
561 | my_entry.eof = 0; | ||
562 | my_entry.fh = nfs_alloc_fhandle(); | ||
563 | my_entry.fattr = nfs_alloc_fattr(); | ||
564 | if (my_entry.fh == NULL || my_entry.fattr == NULL) | ||
565 | goto out_alloc_failed; | ||
566 | |||
567 | desc->entry = &my_entry; | ||
568 | |||
569 | nfs_block_sillyrename(dentry); | 780 | nfs_block_sillyrename(dentry); |
570 | res = nfs_revalidate_mapping(inode, filp->f_mapping); | 781 | res = nfs_revalidate_mapping(inode, filp->f_mapping); |
571 | if (res < 0) | 782 | if (res < 0) |
572 | goto out; | 783 | goto out; |
573 | 784 | ||
574 | while(!desc->entry->eof) { | 785 | while (desc->eof != 1) { |
575 | res = readdir_search_pagecache(desc); | 786 | res = readdir_search_pagecache(desc); |
576 | 787 | ||
577 | if (res == -EBADCOOKIE) { | 788 | if (res == -EBADCOOKIE) { |
578 | /* This means either end of directory */ | 789 | /* This means either end of directory */ |
579 | if (*desc->dir_cookie && desc->entry->cookie != *desc->dir_cookie) { | 790 | if (*desc->dir_cookie && desc->eof == 0) { |
580 | /* Or that the server has 'lost' a cookie */ | 791 | /* Or that the server has 'lost' a cookie */ |
581 | res = uncached_readdir(desc, dirent, filldir); | 792 | res = uncached_readdir(desc, dirent, filldir); |
582 | if (res >= 0) | 793 | if (res >= 0) |
@@ -588,8 +799,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
588 | if (res == -ETOOSMALL && desc->plus) { | 799 | if (res == -ETOOSMALL && desc->plus) { |
589 | clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); | 800 | clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); |
590 | nfs_zap_caches(inode); | 801 | nfs_zap_caches(inode); |
802 | desc->page_index = 0; | ||
591 | desc->plus = 0; | 803 | desc->plus = 0; |
592 | desc->entry->eof = 0; | 804 | desc->eof = 0; |
593 | continue; | 805 | continue; |
594 | } | 806 | } |
595 | if (res < 0) | 807 | if (res < 0) |
@@ -605,9 +817,6 @@ out: | |||
605 | nfs_unblock_sillyrename(dentry); | 817 | nfs_unblock_sillyrename(dentry); |
606 | if (res > 0) | 818 | if (res > 0) |
607 | res = 0; | 819 | res = 0; |
608 | out_alloc_failed: | ||
609 | nfs_free_fattr(my_entry.fattr); | ||
610 | nfs_free_fhandle(my_entry.fh); | ||
611 | dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n", | 820 | dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n", |
612 | dentry->d_parent->d_name.name, dentry->d_name.name, | 821 | dentry->d_parent->d_name.name, dentry->d_name.name, |
613 | res); | 822 | res); |
@@ -1029,10 +1238,63 @@ static int is_atomic_open(struct nameidata *nd) | |||
1029 | return 1; | 1238 | return 1; |
1030 | } | 1239 | } |
1031 | 1240 | ||
1241 | static struct nfs_open_context *nameidata_to_nfs_open_context(struct dentry *dentry, struct nameidata *nd) | ||
1242 | { | ||
1243 | struct path path = { | ||
1244 | .mnt = nd->path.mnt, | ||
1245 | .dentry = dentry, | ||
1246 | }; | ||
1247 | struct nfs_open_context *ctx; | ||
1248 | struct rpc_cred *cred; | ||
1249 | fmode_t fmode = nd->intent.open.flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC); | ||
1250 | |||
1251 | cred = rpc_lookup_cred(); | ||
1252 | if (IS_ERR(cred)) | ||
1253 | return ERR_CAST(cred); | ||
1254 | ctx = alloc_nfs_open_context(&path, cred, fmode); | ||
1255 | put_rpccred(cred); | ||
1256 | if (ctx == NULL) | ||
1257 | return ERR_PTR(-ENOMEM); | ||
1258 | return ctx; | ||
1259 | } | ||
1260 | |||
1261 | static int do_open(struct inode *inode, struct file *filp) | ||
1262 | { | ||
1263 | nfs_fscache_set_inode_cookie(inode, filp); | ||
1264 | return 0; | ||
1265 | } | ||
1266 | |||
1267 | static int nfs_intent_set_file(struct nameidata *nd, struct nfs_open_context *ctx) | ||
1268 | { | ||
1269 | struct file *filp; | ||
1270 | int ret = 0; | ||
1271 | |||
1272 | /* If the open_intent is for execute, we have an extra check to make */ | ||
1273 | if (ctx->mode & FMODE_EXEC) { | ||
1274 | ret = nfs_may_open(ctx->path.dentry->d_inode, | ||
1275 | ctx->cred, | ||
1276 | nd->intent.open.flags); | ||
1277 | if (ret < 0) | ||
1278 | goto out; | ||
1279 | } | ||
1280 | filp = lookup_instantiate_filp(nd, ctx->path.dentry, do_open); | ||
1281 | if (IS_ERR(filp)) | ||
1282 | ret = PTR_ERR(filp); | ||
1283 | else | ||
1284 | nfs_file_set_open_context(filp, ctx); | ||
1285 | out: | ||
1286 | put_nfs_open_context(ctx); | ||
1287 | return ret; | ||
1288 | } | ||
1289 | |||
1032 | static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | 1290 | static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) |
1033 | { | 1291 | { |
1292 | struct nfs_open_context *ctx; | ||
1293 | struct iattr attr; | ||
1034 | struct dentry *res = NULL; | 1294 | struct dentry *res = NULL; |
1035 | int error; | 1295 | struct inode *inode; |
1296 | int open_flags; | ||
1297 | int err; | ||
1036 | 1298 | ||
1037 | dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n", | 1299 | dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n", |
1038 | dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); | 1300 | dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); |
@@ -1054,13 +1316,32 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry | |||
1054 | goto out; | 1316 | goto out; |
1055 | } | 1317 | } |
1056 | 1318 | ||
1319 | ctx = nameidata_to_nfs_open_context(dentry, nd); | ||
1320 | res = ERR_CAST(ctx); | ||
1321 | if (IS_ERR(ctx)) | ||
1322 | goto out; | ||
1323 | |||
1324 | open_flags = nd->intent.open.flags; | ||
1325 | if (nd->flags & LOOKUP_CREATE) { | ||
1326 | attr.ia_mode = nd->intent.open.create_mode; | ||
1327 | attr.ia_valid = ATTR_MODE; | ||
1328 | if (!IS_POSIXACL(dir)) | ||
1329 | attr.ia_mode &= ~current_umask(); | ||
1330 | } else { | ||
1331 | open_flags &= ~(O_EXCL | O_CREAT); | ||
1332 | attr.ia_valid = 0; | ||
1333 | } | ||
1334 | |||
1057 | /* Open the file on the server */ | 1335 | /* Open the file on the server */ |
1058 | res = nfs4_atomic_open(dir, dentry, nd); | 1336 | nfs_block_sillyrename(dentry->d_parent); |
1059 | if (IS_ERR(res)) { | 1337 | inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr); |
1060 | error = PTR_ERR(res); | 1338 | if (IS_ERR(inode)) { |
1061 | switch (error) { | 1339 | nfs_unblock_sillyrename(dentry->d_parent); |
1340 | put_nfs_open_context(ctx); | ||
1341 | switch (PTR_ERR(inode)) { | ||
1062 | /* Make a negative dentry */ | 1342 | /* Make a negative dentry */ |
1063 | case -ENOENT: | 1343 | case -ENOENT: |
1344 | d_add(dentry, NULL); | ||
1064 | res = NULL; | 1345 | res = NULL; |
1065 | goto out; | 1346 | goto out; |
1066 | /* This turned out not to be a regular file */ | 1347 | /* This turned out not to be a regular file */ |
@@ -1072,11 +1353,25 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry | |||
1072 | goto no_open; | 1353 | goto no_open; |
1073 | /* case -EINVAL: */ | 1354 | /* case -EINVAL: */ |
1074 | default: | 1355 | default: |
1356 | res = ERR_CAST(inode); | ||
1075 | goto out; | 1357 | goto out; |
1076 | } | 1358 | } |
1077 | } else if (res != NULL) | 1359 | } |
1360 | res = d_add_unique(dentry, inode); | ||
1361 | nfs_unblock_sillyrename(dentry->d_parent); | ||
1362 | if (res != NULL) { | ||
1363 | dput(ctx->path.dentry); | ||
1364 | ctx->path.dentry = dget(res); | ||
1078 | dentry = res; | 1365 | dentry = res; |
1366 | } | ||
1367 | err = nfs_intent_set_file(nd, ctx); | ||
1368 | if (err < 0) { | ||
1369 | if (res != NULL) | ||
1370 | dput(res); | ||
1371 | return ERR_PTR(err); | ||
1372 | } | ||
1079 | out: | 1373 | out: |
1374 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
1080 | return res; | 1375 | return res; |
1081 | no_open: | 1376 | no_open: |
1082 | return nfs_lookup(dir, dentry, nd); | 1377 | return nfs_lookup(dir, dentry, nd); |
@@ -1087,12 +1382,15 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
1087 | struct dentry *parent = NULL; | 1382 | struct dentry *parent = NULL; |
1088 | struct inode *inode = dentry->d_inode; | 1383 | struct inode *inode = dentry->d_inode; |
1089 | struct inode *dir; | 1384 | struct inode *dir; |
1385 | struct nfs_open_context *ctx; | ||
1090 | int openflags, ret = 0; | 1386 | int openflags, ret = 0; |
1091 | 1387 | ||
1092 | if (!is_atomic_open(nd) || d_mountpoint(dentry)) | 1388 | if (!is_atomic_open(nd) || d_mountpoint(dentry)) |
1093 | goto no_open; | 1389 | goto no_open; |
1390 | |||
1094 | parent = dget_parent(dentry); | 1391 | parent = dget_parent(dentry); |
1095 | dir = parent->d_inode; | 1392 | dir = parent->d_inode; |
1393 | |||
1096 | /* We can't create new files in nfs_open_revalidate(), so we | 1394 | /* We can't create new files in nfs_open_revalidate(), so we |
1097 | * optimize away revalidation of negative dentries. | 1395 | * optimize away revalidation of negative dentries. |
1098 | */ | 1396 | */ |
@@ -1112,99 +1410,96 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
1112 | /* We can't create new files, or truncate existing ones here */ | 1410 | /* We can't create new files, or truncate existing ones here */ |
1113 | openflags &= ~(O_CREAT|O_EXCL|O_TRUNC); | 1411 | openflags &= ~(O_CREAT|O_EXCL|O_TRUNC); |
1114 | 1412 | ||
1413 | ctx = nameidata_to_nfs_open_context(dentry, nd); | ||
1414 | ret = PTR_ERR(ctx); | ||
1415 | if (IS_ERR(ctx)) | ||
1416 | goto out; | ||
1115 | /* | 1417 | /* |
1116 | * Note: we're not holding inode->i_mutex and so may be racing with | 1418 | * Note: we're not holding inode->i_mutex and so may be racing with |
1117 | * operations that change the directory. We therefore save the | 1419 | * operations that change the directory. We therefore save the |
1118 | * change attribute *before* we do the RPC call. | 1420 | * change attribute *before* we do the RPC call. |
1119 | */ | 1421 | */ |
1120 | ret = nfs4_open_revalidate(dir, dentry, openflags, nd); | 1422 | inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, NULL); |
1423 | if (IS_ERR(inode)) { | ||
1424 | ret = PTR_ERR(inode); | ||
1425 | switch (ret) { | ||
1426 | case -EPERM: | ||
1427 | case -EACCES: | ||
1428 | case -EDQUOT: | ||
1429 | case -ENOSPC: | ||
1430 | case -EROFS: | ||
1431 | goto out_put_ctx; | ||
1432 | default: | ||
1433 | goto out_drop; | ||
1434 | } | ||
1435 | } | ||
1436 | iput(inode); | ||
1437 | if (inode != dentry->d_inode) | ||
1438 | goto out_drop; | ||
1439 | |||
1440 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
1441 | ret = nfs_intent_set_file(nd, ctx); | ||
1442 | if (ret >= 0) | ||
1443 | ret = 1; | ||
1121 | out: | 1444 | out: |
1122 | dput(parent); | 1445 | dput(parent); |
1123 | if (!ret) | ||
1124 | d_drop(dentry); | ||
1125 | return ret; | 1446 | return ret; |
1447 | out_drop: | ||
1448 | d_drop(dentry); | ||
1449 | ret = 0; | ||
1450 | out_put_ctx: | ||
1451 | put_nfs_open_context(ctx); | ||
1452 | goto out; | ||
1453 | |||
1126 | no_open_dput: | 1454 | no_open_dput: |
1127 | dput(parent); | 1455 | dput(parent); |
1128 | no_open: | 1456 | no_open: |
1129 | return nfs_lookup_revalidate(dentry, nd); | 1457 | return nfs_lookup_revalidate(dentry, nd); |
1130 | } | 1458 | } |
1131 | #endif /* CONFIG_NFSV4 */ | ||
1132 | 1459 | ||
1133 | static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc) | 1460 | static int nfs_open_create(struct inode *dir, struct dentry *dentry, int mode, |
1461 | struct nameidata *nd) | ||
1134 | { | 1462 | { |
1135 | struct dentry *parent = desc->file->f_path.dentry; | 1463 | struct nfs_open_context *ctx = NULL; |
1136 | struct inode *dir = parent->d_inode; | 1464 | struct iattr attr; |
1137 | struct nfs_entry *entry = desc->entry; | 1465 | int error; |
1138 | struct dentry *dentry, *alias; | 1466 | int open_flags = 0; |
1139 | struct qstr name = { | ||
1140 | .name = entry->name, | ||
1141 | .len = entry->len, | ||
1142 | }; | ||
1143 | struct inode *inode; | ||
1144 | unsigned long verf = nfs_save_change_attribute(dir); | ||
1145 | 1467 | ||
1146 | switch (name.len) { | 1468 | dfprintk(VFS, "NFS: create(%s/%ld), %s\n", |
1147 | case 2: | 1469 | dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); |
1148 | if (name.name[0] == '.' && name.name[1] == '.') | ||
1149 | return dget_parent(parent); | ||
1150 | break; | ||
1151 | case 1: | ||
1152 | if (name.name[0] == '.') | ||
1153 | return dget(parent); | ||
1154 | } | ||
1155 | 1470 | ||
1156 | spin_lock(&dir->i_lock); | 1471 | attr.ia_mode = mode; |
1157 | if (NFS_I(dir)->cache_validity & NFS_INO_INVALID_DATA) { | 1472 | attr.ia_valid = ATTR_MODE; |
1158 | spin_unlock(&dir->i_lock); | ||
1159 | return NULL; | ||
1160 | } | ||
1161 | spin_unlock(&dir->i_lock); | ||
1162 | 1473 | ||
1163 | name.hash = full_name_hash(name.name, name.len); | 1474 | if ((nd->flags & LOOKUP_CREATE) != 0) { |
1164 | dentry = d_lookup(parent, &name); | 1475 | open_flags = nd->intent.open.flags; |
1165 | if (dentry != NULL) { | ||
1166 | /* Is this a positive dentry that matches the readdir info? */ | ||
1167 | if (dentry->d_inode != NULL && | ||
1168 | (NFS_FILEID(dentry->d_inode) == entry->ino || | ||
1169 | d_mountpoint(dentry))) { | ||
1170 | if (!desc->plus || entry->fh->size == 0) | ||
1171 | return dentry; | ||
1172 | if (nfs_compare_fh(NFS_FH(dentry->d_inode), | ||
1173 | entry->fh) == 0) | ||
1174 | goto out_renew; | ||
1175 | } | ||
1176 | /* No, so d_drop to allow one to be created */ | ||
1177 | d_drop(dentry); | ||
1178 | dput(dentry); | ||
1179 | } | ||
1180 | if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR)) | ||
1181 | return NULL; | ||
1182 | if (name.len > NFS_SERVER(dir)->namelen) | ||
1183 | return NULL; | ||
1184 | /* Note: caller is already holding the dir->i_mutex! */ | ||
1185 | dentry = d_alloc(parent, &name); | ||
1186 | if (dentry == NULL) | ||
1187 | return NULL; | ||
1188 | dentry->d_op = NFS_PROTO(dir)->dentry_ops; | ||
1189 | inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr); | ||
1190 | if (IS_ERR(inode)) { | ||
1191 | dput(dentry); | ||
1192 | return NULL; | ||
1193 | } | ||
1194 | 1476 | ||
1195 | alias = d_materialise_unique(dentry, inode); | 1477 | ctx = nameidata_to_nfs_open_context(dentry, nd); |
1196 | if (alias != NULL) { | 1478 | error = PTR_ERR(ctx); |
1197 | dput(dentry); | 1479 | if (IS_ERR(ctx)) |
1198 | if (IS_ERR(alias)) | 1480 | goto out_err_drop; |
1199 | return NULL; | ||
1200 | dentry = alias; | ||
1201 | } | 1481 | } |
1202 | 1482 | ||
1203 | out_renew: | 1483 | error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, ctx); |
1204 | nfs_set_verifier(dentry, verf); | 1484 | if (error != 0) |
1205 | return dentry; | 1485 | goto out_put_ctx; |
1486 | if (ctx != NULL) { | ||
1487 | error = nfs_intent_set_file(nd, ctx); | ||
1488 | if (error < 0) | ||
1489 | goto out_err; | ||
1490 | } | ||
1491 | return 0; | ||
1492 | out_put_ctx: | ||
1493 | if (ctx != NULL) | ||
1494 | put_nfs_open_context(ctx); | ||
1495 | out_err_drop: | ||
1496 | d_drop(dentry); | ||
1497 | out_err: | ||
1498 | return error; | ||
1206 | } | 1499 | } |
1207 | 1500 | ||
1501 | #endif /* CONFIG_NFSV4 */ | ||
1502 | |||
1208 | /* | 1503 | /* |
1209 | * Code common to create, mkdir, and mknod. | 1504 | * Code common to create, mkdir, and mknod. |
1210 | */ | 1505 | */ |
@@ -1258,7 +1553,6 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, | |||
1258 | { | 1553 | { |
1259 | struct iattr attr; | 1554 | struct iattr attr; |
1260 | int error; | 1555 | int error; |
1261 | int open_flags = 0; | ||
1262 | 1556 | ||
1263 | dfprintk(VFS, "NFS: create(%s/%ld), %s\n", | 1557 | dfprintk(VFS, "NFS: create(%s/%ld), %s\n", |
1264 | dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); | 1558 | dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); |
@@ -1266,10 +1560,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, | |||
1266 | attr.ia_mode = mode; | 1560 | attr.ia_mode = mode; |
1267 | attr.ia_valid = ATTR_MODE; | 1561 | attr.ia_valid = ATTR_MODE; |
1268 | 1562 | ||
1269 | if ((nd->flags & LOOKUP_CREATE) != 0) | 1563 | error = NFS_PROTO(dir)->create(dir, dentry, &attr, 0, NULL); |
1270 | open_flags = nd->intent.open.flags; | ||
1271 | |||
1272 | error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd); | ||
1273 | if (error != 0) | 1564 | if (error != 0) |
1274 | goto out_err; | 1565 | goto out_err; |
1275 | return 0; | 1566 | return 0; |
@@ -1351,76 +1642,6 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1351 | return error; | 1642 | return error; |
1352 | } | 1643 | } |
1353 | 1644 | ||
1354 | static int nfs_sillyrename(struct inode *dir, struct dentry *dentry) | ||
1355 | { | ||
1356 | static unsigned int sillycounter; | ||
1357 | const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; | ||
1358 | const int countersize = sizeof(sillycounter)*2; | ||
1359 | const int slen = sizeof(".nfs")+fileidsize+countersize-1; | ||
1360 | char silly[slen+1]; | ||
1361 | struct qstr qsilly; | ||
1362 | struct dentry *sdentry; | ||
1363 | int error = -EIO; | ||
1364 | |||
1365 | dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", | ||
1366 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
1367 | atomic_read(&dentry->d_count)); | ||
1368 | nfs_inc_stats(dir, NFSIOS_SILLYRENAME); | ||
1369 | |||
1370 | /* | ||
1371 | * We don't allow a dentry to be silly-renamed twice. | ||
1372 | */ | ||
1373 | error = -EBUSY; | ||
1374 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
1375 | goto out; | ||
1376 | |||
1377 | sprintf(silly, ".nfs%*.*Lx", | ||
1378 | fileidsize, fileidsize, | ||
1379 | (unsigned long long)NFS_FILEID(dentry->d_inode)); | ||
1380 | |||
1381 | /* Return delegation in anticipation of the rename */ | ||
1382 | nfs_inode_return_delegation(dentry->d_inode); | ||
1383 | |||
1384 | sdentry = NULL; | ||
1385 | do { | ||
1386 | char *suffix = silly + slen - countersize; | ||
1387 | |||
1388 | dput(sdentry); | ||
1389 | sillycounter++; | ||
1390 | sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); | ||
1391 | |||
1392 | dfprintk(VFS, "NFS: trying to rename %s to %s\n", | ||
1393 | dentry->d_name.name, silly); | ||
1394 | |||
1395 | sdentry = lookup_one_len(silly, dentry->d_parent, slen); | ||
1396 | /* | ||
1397 | * N.B. Better to return EBUSY here ... it could be | ||
1398 | * dangerous to delete the file while it's in use. | ||
1399 | */ | ||
1400 | if (IS_ERR(sdentry)) | ||
1401 | goto out; | ||
1402 | } while(sdentry->d_inode != NULL); /* need negative lookup */ | ||
1403 | |||
1404 | qsilly.name = silly; | ||
1405 | qsilly.len = strlen(silly); | ||
1406 | if (dentry->d_inode) { | ||
1407 | error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, | ||
1408 | dir, &qsilly); | ||
1409 | nfs_mark_for_revalidate(dentry->d_inode); | ||
1410 | } else | ||
1411 | error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, | ||
1412 | dir, &qsilly); | ||
1413 | if (!error) { | ||
1414 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
1415 | d_move(dentry, sdentry); | ||
1416 | error = nfs_async_unlink(dir, dentry); | ||
1417 | /* If we return 0 we don't unlink */ | ||
1418 | } | ||
1419 | dput(sdentry); | ||
1420 | out: | ||
1421 | return error; | ||
1422 | } | ||
1423 | |||
1424 | /* | 1645 | /* |
1425 | * Remove a file after making sure there are no pending writes, | 1646 | * Remove a file after making sure there are no pending writes, |
1426 | * and after checking that the file has only one user. | 1647 | * and after checking that the file has only one user. |
@@ -1711,14 +1932,14 @@ static void nfs_access_free_list(struct list_head *head) | |||
1711 | int nfs_access_cache_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) | 1932 | int nfs_access_cache_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) |
1712 | { | 1933 | { |
1713 | LIST_HEAD(head); | 1934 | LIST_HEAD(head); |
1714 | struct nfs_inode *nfsi; | 1935 | struct nfs_inode *nfsi, *next; |
1715 | struct nfs_access_entry *cache; | 1936 | struct nfs_access_entry *cache; |
1716 | 1937 | ||
1717 | if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) | 1938 | if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) |
1718 | return (nr_to_scan == 0) ? 0 : -1; | 1939 | return (nr_to_scan == 0) ? 0 : -1; |
1719 | 1940 | ||
1720 | spin_lock(&nfs_access_lru_lock); | 1941 | spin_lock(&nfs_access_lru_lock); |
1721 | list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) { | 1942 | list_for_each_entry_safe(nfsi, next, &nfs_access_lru_list, access_cache_inode_lru) { |
1722 | struct inode *inode; | 1943 | struct inode *inode; |
1723 | 1944 | ||
1724 | if (nr_to_scan-- == 0) | 1945 | if (nr_to_scan-- == 0) |
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 05bf3c0dc751..e18c31e08a28 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
@@ -551,7 +551,7 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
551 | struct file *filp = vma->vm_file; | 551 | struct file *filp = vma->vm_file; |
552 | struct dentry *dentry = filp->f_path.dentry; | 552 | struct dentry *dentry = filp->f_path.dentry; |
553 | unsigned pagelen; | 553 | unsigned pagelen; |
554 | int ret = -EINVAL; | 554 | int ret = VM_FAULT_NOPAGE; |
555 | struct address_space *mapping; | 555 | struct address_space *mapping; |
556 | 556 | ||
557 | dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%s/%s(%ld), offset %lld)\n", | 557 | dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%s/%s(%ld), offset %lld)\n", |
@@ -567,21 +567,20 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
567 | if (mapping != dentry->d_inode->i_mapping) | 567 | if (mapping != dentry->d_inode->i_mapping) |
568 | goto out_unlock; | 568 | goto out_unlock; |
569 | 569 | ||
570 | ret = 0; | ||
571 | pagelen = nfs_page_length(page); | 570 | pagelen = nfs_page_length(page); |
572 | if (pagelen == 0) | 571 | if (pagelen == 0) |
573 | goto out_unlock; | 572 | goto out_unlock; |
574 | 573 | ||
575 | ret = nfs_flush_incompatible(filp, page); | 574 | ret = VM_FAULT_LOCKED; |
576 | if (ret != 0) | 575 | if (nfs_flush_incompatible(filp, page) == 0 && |
577 | goto out_unlock; | 576 | nfs_updatepage(filp, page, 0, pagelen) == 0) |
577 | goto out; | ||
578 | 578 | ||
579 | ret = nfs_updatepage(filp, page, 0, pagelen); | 579 | ret = VM_FAULT_SIGBUS; |
580 | out_unlock: | 580 | out_unlock: |
581 | if (!ret) | ||
582 | return VM_FAULT_LOCKED; | ||
583 | unlock_page(page); | 581 | unlock_page(page); |
584 | return VM_FAULT_SIGBUS; | 582 | out: |
583 | return ret; | ||
585 | } | 584 | } |
586 | 585 | ||
587 | static const struct vm_operations_struct nfs_file_vm_ops = { | 586 | static const struct vm_operations_struct nfs_file_vm_ops = { |
@@ -684,7 +683,8 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, | |||
684 | return ret; | 683 | return ret; |
685 | } | 684 | } |
686 | 685 | ||
687 | static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) | 686 | static int |
687 | do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) | ||
688 | { | 688 | { |
689 | struct inode *inode = filp->f_mapping->host; | 689 | struct inode *inode = filp->f_mapping->host; |
690 | int status = 0; | 690 | int status = 0; |
@@ -699,7 +699,7 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) | |||
699 | if (nfs_have_delegation(inode, FMODE_READ)) | 699 | if (nfs_have_delegation(inode, FMODE_READ)) |
700 | goto out_noconflict; | 700 | goto out_noconflict; |
701 | 701 | ||
702 | if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) | 702 | if (is_local) |
703 | goto out_noconflict; | 703 | goto out_noconflict; |
704 | 704 | ||
705 | status = NFS_PROTO(inode)->lock(filp, cmd, fl); | 705 | status = NFS_PROTO(inode)->lock(filp, cmd, fl); |
@@ -726,7 +726,8 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl) | |||
726 | return res; | 726 | return res; |
727 | } | 727 | } |
728 | 728 | ||
729 | static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) | 729 | static int |
730 | do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) | ||
730 | { | 731 | { |
731 | struct inode *inode = filp->f_mapping->host; | 732 | struct inode *inode = filp->f_mapping->host; |
732 | int status; | 733 | int status; |
@@ -741,15 +742,24 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) | |||
741 | * If we're signalled while cleaning up locks on process exit, we | 742 | * If we're signalled while cleaning up locks on process exit, we |
742 | * still need to complete the unlock. | 743 | * still need to complete the unlock. |
743 | */ | 744 | */ |
744 | /* Use local locking if mounted with "-onolock" */ | 745 | /* |
745 | if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) | 746 | * Use local locking if mounted with "-onolock" or with appropriate |
747 | * "-olocal_lock=" | ||
748 | */ | ||
749 | if (!is_local) | ||
746 | status = NFS_PROTO(inode)->lock(filp, cmd, fl); | 750 | status = NFS_PROTO(inode)->lock(filp, cmd, fl); |
747 | else | 751 | else |
748 | status = do_vfs_lock(filp, fl); | 752 | status = do_vfs_lock(filp, fl); |
749 | return status; | 753 | return status; |
750 | } | 754 | } |
751 | 755 | ||
752 | static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) | 756 | static int |
757 | is_time_granular(struct timespec *ts) { | ||
758 | return ((ts->tv_sec == 0) && (ts->tv_nsec <= 1000)); | ||
759 | } | ||
760 | |||
761 | static int | ||
762 | do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) | ||
753 | { | 763 | { |
754 | struct inode *inode = filp->f_mapping->host; | 764 | struct inode *inode = filp->f_mapping->host; |
755 | int status; | 765 | int status; |
@@ -762,20 +772,31 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) | |||
762 | if (status != 0) | 772 | if (status != 0) |
763 | goto out; | 773 | goto out; |
764 | 774 | ||
765 | /* Use local locking if mounted with "-onolock" */ | 775 | /* |
766 | if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) | 776 | * Use local locking if mounted with "-onolock" or with appropriate |
777 | * "-olocal_lock=" | ||
778 | */ | ||
779 | if (!is_local) | ||
767 | status = NFS_PROTO(inode)->lock(filp, cmd, fl); | 780 | status = NFS_PROTO(inode)->lock(filp, cmd, fl); |
768 | else | 781 | else |
769 | status = do_vfs_lock(filp, fl); | 782 | status = do_vfs_lock(filp, fl); |
770 | if (status < 0) | 783 | if (status < 0) |
771 | goto out; | 784 | goto out; |
785 | |||
772 | /* | 786 | /* |
773 | * Make sure we clear the cache whenever we try to get the lock. | 787 | * Revalidate the cache if the server has time stamps granular |
788 | * enough to detect subsecond changes. Otherwise, clear the | ||
789 | * cache to prevent missing any changes. | ||
790 | * | ||
774 | * This makes locking act as a cache coherency point. | 791 | * This makes locking act as a cache coherency point. |
775 | */ | 792 | */ |
776 | nfs_sync_mapping(filp->f_mapping); | 793 | nfs_sync_mapping(filp->f_mapping); |
777 | if (!nfs_have_delegation(inode, FMODE_READ)) | 794 | if (!nfs_have_delegation(inode, FMODE_READ)) { |
778 | nfs_zap_caches(inode); | 795 | if (is_time_granular(&NFS_SERVER(inode)->time_delta)) |
796 | __nfs_revalidate_inode(NFS_SERVER(inode), inode); | ||
797 | else | ||
798 | nfs_zap_caches(inode); | ||
799 | } | ||
779 | out: | 800 | out: |
780 | return status; | 801 | return status; |
781 | } | 802 | } |
@@ -787,6 +808,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) | |||
787 | { | 808 | { |
788 | struct inode *inode = filp->f_mapping->host; | 809 | struct inode *inode = filp->f_mapping->host; |
789 | int ret = -ENOLCK; | 810 | int ret = -ENOLCK; |
811 | int is_local = 0; | ||
790 | 812 | ||
791 | dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n", | 813 | dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n", |
792 | filp->f_path.dentry->d_parent->d_name.name, | 814 | filp->f_path.dentry->d_parent->d_name.name, |
@@ -800,6 +822,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) | |||
800 | if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) | 822 | if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) |
801 | goto out_err; | 823 | goto out_err; |
802 | 824 | ||
825 | if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FCNTL) | ||
826 | is_local = 1; | ||
827 | |||
803 | if (NFS_PROTO(inode)->lock_check_bounds != NULL) { | 828 | if (NFS_PROTO(inode)->lock_check_bounds != NULL) { |
804 | ret = NFS_PROTO(inode)->lock_check_bounds(fl); | 829 | ret = NFS_PROTO(inode)->lock_check_bounds(fl); |
805 | if (ret < 0) | 830 | if (ret < 0) |
@@ -807,11 +832,11 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) | |||
807 | } | 832 | } |
808 | 833 | ||
809 | if (IS_GETLK(cmd)) | 834 | if (IS_GETLK(cmd)) |
810 | ret = do_getlk(filp, cmd, fl); | 835 | ret = do_getlk(filp, cmd, fl, is_local); |
811 | else if (fl->fl_type == F_UNLCK) | 836 | else if (fl->fl_type == F_UNLCK) |
812 | ret = do_unlk(filp, cmd, fl); | 837 | ret = do_unlk(filp, cmd, fl, is_local); |
813 | else | 838 | else |
814 | ret = do_setlk(filp, cmd, fl); | 839 | ret = do_setlk(filp, cmd, fl, is_local); |
815 | out_err: | 840 | out_err: |
816 | return ret; | 841 | return ret; |
817 | } | 842 | } |
@@ -821,6 +846,9 @@ out_err: | |||
821 | */ | 846 | */ |
822 | static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) | 847 | static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) |
823 | { | 848 | { |
849 | struct inode *inode = filp->f_mapping->host; | ||
850 | int is_local = 0; | ||
851 | |||
824 | dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n", | 852 | dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n", |
825 | filp->f_path.dentry->d_parent->d_name.name, | 853 | filp->f_path.dentry->d_parent->d_name.name, |
826 | filp->f_path.dentry->d_name.name, | 854 | filp->f_path.dentry->d_name.name, |
@@ -829,14 +857,17 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) | |||
829 | if (!(fl->fl_flags & FL_FLOCK)) | 857 | if (!(fl->fl_flags & FL_FLOCK)) |
830 | return -ENOLCK; | 858 | return -ENOLCK; |
831 | 859 | ||
860 | if (NFS_SERVER(inode)->flags & NFS_MOUNT_LOCAL_FLOCK) | ||
861 | is_local = 1; | ||
862 | |||
832 | /* We're simulating flock() locks using posix locks on the server */ | 863 | /* We're simulating flock() locks using posix locks on the server */ |
833 | fl->fl_owner = (fl_owner_t)filp; | 864 | fl->fl_owner = (fl_owner_t)filp; |
834 | fl->fl_start = 0; | 865 | fl->fl_start = 0; |
835 | fl->fl_end = OFFSET_MAX; | 866 | fl->fl_end = OFFSET_MAX; |
836 | 867 | ||
837 | if (fl->fl_type == F_UNLCK) | 868 | if (fl->fl_type == F_UNLCK) |
838 | return do_unlk(filp, cmd, fl); | 869 | return do_unlk(filp, cmd, fl, is_local); |
839 | return do_setlk(filp, cmd, fl); | 870 | return do_setlk(filp, cmd, fl, is_local); |
840 | } | 871 | } |
841 | 872 | ||
842 | /* | 873 | /* |
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 21a84d45916f..dec47ed8b6b9 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c | |||
@@ -34,6 +34,212 @@ | |||
34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
35 | */ | 35 | */ |
36 | 36 | ||
37 | #ifdef CONFIG_NFS_USE_NEW_IDMAPPER | ||
38 | |||
39 | #include <linux/slab.h> | ||
40 | #include <linux/cred.h> | ||
41 | #include <linux/nfs_idmap.h> | ||
42 | #include <linux/keyctl.h> | ||
43 | #include <linux/key-type.h> | ||
44 | #include <linux/rcupdate.h> | ||
45 | #include <linux/kernel.h> | ||
46 | #include <linux/err.h> | ||
47 | |||
48 | #include <keys/user-type.h> | ||
49 | |||
50 | #define NFS_UINT_MAXLEN 11 | ||
51 | |||
52 | const struct cred *id_resolver_cache; | ||
53 | |||
54 | struct key_type key_type_id_resolver = { | ||
55 | .name = "id_resolver", | ||
56 | .instantiate = user_instantiate, | ||
57 | .match = user_match, | ||
58 | .revoke = user_revoke, | ||
59 | .destroy = user_destroy, | ||
60 | .describe = user_describe, | ||
61 | .read = user_read, | ||
62 | }; | ||
63 | |||
64 | int nfs_idmap_init(void) | ||
65 | { | ||
66 | struct cred *cred; | ||
67 | struct key *keyring; | ||
68 | int ret = 0; | ||
69 | |||
70 | printk(KERN_NOTICE "Registering the %s key type\n", key_type_id_resolver.name); | ||
71 | |||
72 | cred = prepare_kernel_cred(NULL); | ||
73 | if (!cred) | ||
74 | return -ENOMEM; | ||
75 | |||
76 | keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred, | ||
77 | (KEY_POS_ALL & ~KEY_POS_SETATTR) | | ||
78 | KEY_USR_VIEW | KEY_USR_READ, | ||
79 | KEY_ALLOC_NOT_IN_QUOTA); | ||
80 | if (IS_ERR(keyring)) { | ||
81 | ret = PTR_ERR(keyring); | ||
82 | goto failed_put_cred; | ||
83 | } | ||
84 | |||
85 | ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); | ||
86 | if (ret < 0) | ||
87 | goto failed_put_key; | ||
88 | |||
89 | ret = register_key_type(&key_type_id_resolver); | ||
90 | if (ret < 0) | ||
91 | goto failed_put_key; | ||
92 | |||
93 | cred->thread_keyring = keyring; | ||
94 | cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; | ||
95 | id_resolver_cache = cred; | ||
96 | return 0; | ||
97 | |||
98 | failed_put_key: | ||
99 | key_put(keyring); | ||
100 | failed_put_cred: | ||
101 | put_cred(cred); | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | void nfs_idmap_quit(void) | ||
106 | { | ||
107 | key_revoke(id_resolver_cache->thread_keyring); | ||
108 | unregister_key_type(&key_type_id_resolver); | ||
109 | put_cred(id_resolver_cache); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Assemble the description to pass to request_key() | ||
114 | * This function will allocate a new string and update dest to point | ||
115 | * at it. The caller is responsible for freeing dest. | ||
116 | * | ||
117 | * On error 0 is returned. Otherwise, the length of dest is returned. | ||
118 | */ | ||
119 | static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen, | ||
120 | const char *type, size_t typelen, char **desc) | ||
121 | { | ||
122 | char *cp; | ||
123 | size_t desclen = typelen + namelen + 2; | ||
124 | |||
125 | *desc = kmalloc(desclen, GFP_KERNEL); | ||
126 | if (!desc) | ||
127 | return -ENOMEM; | ||
128 | |||
129 | cp = *desc; | ||
130 | memcpy(cp, type, typelen); | ||
131 | cp += typelen; | ||
132 | *cp++ = ':'; | ||
133 | |||
134 | memcpy(cp, name, namelen); | ||
135 | cp += namelen; | ||
136 | *cp = '\0'; | ||
137 | return desclen; | ||
138 | } | ||
139 | |||
140 | static ssize_t nfs_idmap_request_key(const char *name, size_t namelen, | ||
141 | const char *type, void *data, size_t data_size) | ||
142 | { | ||
143 | const struct cred *saved_cred; | ||
144 | struct key *rkey; | ||
145 | char *desc; | ||
146 | struct user_key_payload *payload; | ||
147 | ssize_t ret; | ||
148 | |||
149 | ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc); | ||
150 | if (ret <= 0) | ||
151 | goto out; | ||
152 | |||
153 | saved_cred = override_creds(id_resolver_cache); | ||
154 | rkey = request_key(&key_type_id_resolver, desc, ""); | ||
155 | revert_creds(saved_cred); | ||
156 | kfree(desc); | ||
157 | if (IS_ERR(rkey)) { | ||
158 | ret = PTR_ERR(rkey); | ||
159 | goto out; | ||
160 | } | ||
161 | |||
162 | rcu_read_lock(); | ||
163 | rkey->perm |= KEY_USR_VIEW; | ||
164 | |||
165 | ret = key_validate(rkey); | ||
166 | if (ret < 0) | ||
167 | goto out_up; | ||
168 | |||
169 | payload = rcu_dereference(rkey->payload.data); | ||
170 | if (IS_ERR_OR_NULL(payload)) { | ||
171 | ret = PTR_ERR(payload); | ||
172 | goto out_up; | ||
173 | } | ||
174 | |||
175 | ret = payload->datalen; | ||
176 | if (ret > 0 && ret <= data_size) | ||
177 | memcpy(data, payload->data, ret); | ||
178 | else | ||
179 | ret = -EINVAL; | ||
180 | |||
181 | out_up: | ||
182 | rcu_read_unlock(); | ||
183 | key_put(rkey); | ||
184 | out: | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | |||
189 | /* ID -> Name */ | ||
190 | static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen) | ||
191 | { | ||
192 | char id_str[NFS_UINT_MAXLEN]; | ||
193 | int id_len; | ||
194 | ssize_t ret; | ||
195 | |||
196 | id_len = snprintf(id_str, sizeof(id_str), "%u", id); | ||
197 | ret = nfs_idmap_request_key(id_str, id_len, type, buf, buflen); | ||
198 | if (ret < 0) | ||
199 | return -EINVAL; | ||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | /* Name -> ID */ | ||
204 | static int nfs_idmap_lookup_id(const char *name, size_t namelen, | ||
205 | const char *type, __u32 *id) | ||
206 | { | ||
207 | char id_str[NFS_UINT_MAXLEN]; | ||
208 | long id_long; | ||
209 | ssize_t data_size; | ||
210 | int ret = 0; | ||
211 | |||
212 | data_size = nfs_idmap_request_key(name, namelen, type, id_str, NFS_UINT_MAXLEN); | ||
213 | if (data_size <= 0) { | ||
214 | ret = -EINVAL; | ||
215 | } else { | ||
216 | ret = strict_strtol(id_str, 10, &id_long); | ||
217 | *id = (__u32)id_long; | ||
218 | } | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | int nfs_map_name_to_uid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *uid) | ||
223 | { | ||
224 | return nfs_idmap_lookup_id(name, namelen, "uid", uid); | ||
225 | } | ||
226 | |||
227 | int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namelen, __u32 *gid) | ||
228 | { | ||
229 | return nfs_idmap_lookup_id(name, namelen, "gid", gid); | ||
230 | } | ||
231 | |||
232 | int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) | ||
233 | { | ||
234 | return nfs_idmap_lookup_name(uid, "user", buf, buflen); | ||
235 | } | ||
236 | int nfs_map_gid_to_group(struct nfs_client *clp, __u32 gid, char *buf, size_t buflen) | ||
237 | { | ||
238 | return nfs_idmap_lookup_name(gid, "group", buf, buflen); | ||
239 | } | ||
240 | |||
241 | #else /* CONFIG_NFS_USE_IDMAPPER not defined */ | ||
242 | |||
37 | #include <linux/module.h> | 243 | #include <linux/module.h> |
38 | #include <linux/mutex.h> | 244 | #include <linux/mutex.h> |
39 | #include <linux/init.h> | 245 | #include <linux/init.h> |
@@ -503,16 +709,17 @@ int nfs_map_group_to_gid(struct nfs_client *clp, const char *name, size_t namele | |||
503 | return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); | 709 | return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); |
504 | } | 710 | } |
505 | 711 | ||
506 | int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf) | 712 | int nfs_map_uid_to_name(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) |
507 | { | 713 | { |
508 | struct idmap *idmap = clp->cl_idmap; | 714 | struct idmap *idmap = clp->cl_idmap; |
509 | 715 | ||
510 | return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); | 716 | return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); |
511 | } | 717 | } |
512 | int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf) | 718 | int nfs_map_gid_to_group(struct nfs_client *clp, __u32 uid, char *buf, size_t buflen) |
513 | { | 719 | { |
514 | struct idmap *idmap = clp->cl_idmap; | 720 | struct idmap *idmap = clp->cl_idmap; |
515 | 721 | ||
516 | return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); | 722 | return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); |
517 | } | 723 | } |
518 | 724 | ||
725 | #endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ | ||
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 7d2d6c72aa78..6eec28656415 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -234,9 +234,6 @@ nfs_init_locked(struct inode *inode, void *opaque) | |||
234 | return 0; | 234 | return 0; |
235 | } | 235 | } |
236 | 236 | ||
237 | /* Don't use READDIRPLUS on directories that we believe are too large */ | ||
238 | #define NFS_LIMIT_READDIRPLUS (8*PAGE_SIZE) | ||
239 | |||
240 | /* | 237 | /* |
241 | * This is our front-end to iget that looks up inodes by file handle | 238 | * This is our front-end to iget that looks up inodes by file handle |
242 | * instead of inode number. | 239 | * instead of inode number. |
@@ -291,8 +288,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) | |||
291 | } else if (S_ISDIR(inode->i_mode)) { | 288 | } else if (S_ISDIR(inode->i_mode)) { |
292 | inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops; | 289 | inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops; |
293 | inode->i_fop = &nfs_dir_operations; | 290 | inode->i_fop = &nfs_dir_operations; |
294 | if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) | 291 | if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)) |
295 | && fattr->size <= NFS_LIMIT_READDIRPLUS) | ||
296 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); | 292 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); |
297 | /* Deal with crossing mountpoints */ | 293 | /* Deal with crossing mountpoints */ |
298 | if ((fattr->valid & NFS_ATTR_FATTR_FSID) | 294 | if ((fattr->valid & NFS_ATTR_FATTR_FSID) |
@@ -623,7 +619,7 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync) | |||
623 | nfs_revalidate_inode(server, inode); | 619 | nfs_revalidate_inode(server, inode); |
624 | } | 620 | } |
625 | 621 | ||
626 | static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cred *cred) | 622 | struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cred *cred, fmode_t f_mode) |
627 | { | 623 | { |
628 | struct nfs_open_context *ctx; | 624 | struct nfs_open_context *ctx; |
629 | 625 | ||
@@ -633,11 +629,13 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct | |||
633 | path_get(&ctx->path); | 629 | path_get(&ctx->path); |
634 | ctx->cred = get_rpccred(cred); | 630 | ctx->cred = get_rpccred(cred); |
635 | ctx->state = NULL; | 631 | ctx->state = NULL; |
632 | ctx->mode = f_mode; | ||
636 | ctx->flags = 0; | 633 | ctx->flags = 0; |
637 | ctx->error = 0; | 634 | ctx->error = 0; |
638 | ctx->dir_cookie = 0; | 635 | ctx->dir_cookie = 0; |
639 | nfs_init_lock_context(&ctx->lock_context); | 636 | nfs_init_lock_context(&ctx->lock_context); |
640 | ctx->lock_context.open_context = ctx; | 637 | ctx->lock_context.open_context = ctx; |
638 | INIT_LIST_HEAD(&ctx->list); | ||
641 | } | 639 | } |
642 | return ctx; | 640 | return ctx; |
643 | } | 641 | } |
@@ -653,11 +651,15 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) | |||
653 | { | 651 | { |
654 | struct inode *inode = ctx->path.dentry->d_inode; | 652 | struct inode *inode = ctx->path.dentry->d_inode; |
655 | 653 | ||
656 | if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock)) | 654 | if (!list_empty(&ctx->list)) { |
655 | if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock)) | ||
656 | return; | ||
657 | list_del(&ctx->list); | ||
658 | spin_unlock(&inode->i_lock); | ||
659 | } else if (!atomic_dec_and_test(&ctx->lock_context.count)) | ||
657 | return; | 660 | return; |
658 | list_del(&ctx->list); | 661 | if (inode != NULL) |
659 | spin_unlock(&inode->i_lock); | 662 | NFS_PROTO(inode)->close_context(ctx, is_sync); |
660 | NFS_PROTO(inode)->close_context(ctx, is_sync); | ||
661 | if (ctx->cred != NULL) | 663 | if (ctx->cred != NULL) |
662 | put_rpccred(ctx->cred); | 664 | put_rpccred(ctx->cred); |
663 | path_put(&ctx->path); | 665 | path_put(&ctx->path); |
@@ -673,7 +675,7 @@ void put_nfs_open_context(struct nfs_open_context *ctx) | |||
673 | * Ensure that mmap has a recent RPC credential for use when writing out | 675 | * Ensure that mmap has a recent RPC credential for use when writing out |
674 | * shared pages | 676 | * shared pages |
675 | */ | 677 | */ |
676 | static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) | 678 | void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) |
677 | { | 679 | { |
678 | struct inode *inode = filp->f_path.dentry->d_inode; | 680 | struct inode *inode = filp->f_path.dentry->d_inode; |
679 | struct nfs_inode *nfsi = NFS_I(inode); | 681 | struct nfs_inode *nfsi = NFS_I(inode); |
@@ -730,11 +732,10 @@ int nfs_open(struct inode *inode, struct file *filp) | |||
730 | cred = rpc_lookup_cred(); | 732 | cred = rpc_lookup_cred(); |
731 | if (IS_ERR(cred)) | 733 | if (IS_ERR(cred)) |
732 | return PTR_ERR(cred); | 734 | return PTR_ERR(cred); |
733 | ctx = alloc_nfs_open_context(&filp->f_path, cred); | 735 | ctx = alloc_nfs_open_context(&filp->f_path, cred, filp->f_mode); |
734 | put_rpccred(cred); | 736 | put_rpccred(cred); |
735 | if (ctx == NULL) | 737 | if (ctx == NULL) |
736 | return -ENOMEM; | 738 | return -ENOMEM; |
737 | ctx->mode = filp->f_mode; | ||
738 | nfs_file_set_open_context(filp, ctx); | 739 | nfs_file_set_open_context(filp, ctx); |
739 | put_nfs_open_context(ctx); | 740 | put_nfs_open_context(ctx); |
740 | nfs_fscache_set_inode_cookie(inode, filp); | 741 | nfs_fscache_set_inode_cookie(inode, filp); |
@@ -1493,7 +1494,7 @@ static int nfsiod_start(void) | |||
1493 | { | 1494 | { |
1494 | struct workqueue_struct *wq; | 1495 | struct workqueue_struct *wq; |
1495 | dprintk("RPC: creating workqueue nfsiod\n"); | 1496 | dprintk("RPC: creating workqueue nfsiod\n"); |
1496 | wq = create_singlethread_workqueue("nfsiod"); | 1497 | wq = alloc_workqueue("nfsiod", WQ_RESCUER, 0); |
1497 | if (wq == NULL) | 1498 | if (wq == NULL) |
1498 | return -ENOMEM; | 1499 | return -ENOMEM; |
1499 | nfsiod_workqueue = wq; | 1500 | nfsiod_workqueue = wq; |
@@ -1521,6 +1522,10 @@ static int __init init_nfs_fs(void) | |||
1521 | { | 1522 | { |
1522 | int err; | 1523 | int err; |
1523 | 1524 | ||
1525 | err = nfs_idmap_init(); | ||
1526 | if (err < 0) | ||
1527 | goto out9; | ||
1528 | |||
1524 | err = nfs_dns_resolver_init(); | 1529 | err = nfs_dns_resolver_init(); |
1525 | if (err < 0) | 1530 | if (err < 0) |
1526 | goto out8; | 1531 | goto out8; |
@@ -1585,6 +1590,8 @@ out6: | |||
1585 | out7: | 1590 | out7: |
1586 | nfs_dns_resolver_destroy(); | 1591 | nfs_dns_resolver_destroy(); |
1587 | out8: | 1592 | out8: |
1593 | nfs_idmap_quit(); | ||
1594 | out9: | ||
1588 | return err; | 1595 | return err; |
1589 | } | 1596 | } |
1590 | 1597 | ||
@@ -1597,6 +1604,7 @@ static void __exit exit_nfs_fs(void) | |||
1597 | nfs_destroy_nfspagecache(); | 1604 | nfs_destroy_nfspagecache(); |
1598 | nfs_fscache_unregister(); | 1605 | nfs_fscache_unregister(); |
1599 | nfs_dns_resolver_destroy(); | 1606 | nfs_dns_resolver_destroy(); |
1607 | nfs_idmap_quit(); | ||
1600 | #ifdef CONFIG_PROC_FS | 1608 | #ifdef CONFIG_PROC_FS |
1601 | rpc_proc_unregister("nfs"); | 1609 | rpc_proc_unregister("nfs"); |
1602 | #endif | 1610 | #endif |
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index c961bc92c107..db08ff3ff454 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -63,6 +63,12 @@ struct nfs_clone_mount { | |||
63 | #define NFS_UNSPEC_PORT (-1) | 63 | #define NFS_UNSPEC_PORT (-1) |
64 | 64 | ||
65 | /* | 65 | /* |
66 | * Maximum number of pages that readdir can use for creating | ||
67 | * a vmapped array of pages. | ||
68 | */ | ||
69 | #define NFS_MAX_READDIR_PAGES 8 | ||
70 | |||
71 | /* | ||
66 | * In-kernel mount arguments | 72 | * In-kernel mount arguments |
67 | */ | 73 | */ |
68 | struct nfs_parsed_mount_data { | 74 | struct nfs_parsed_mount_data { |
@@ -181,15 +187,15 @@ extern void nfs_destroy_directcache(void); | |||
181 | /* nfs2xdr.c */ | 187 | /* nfs2xdr.c */ |
182 | extern int nfs_stat_to_errno(int); | 188 | extern int nfs_stat_to_errno(int); |
183 | extern struct rpc_procinfo nfs_procedures[]; | 189 | extern struct rpc_procinfo nfs_procedures[]; |
184 | extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int); | 190 | extern __be32 *nfs_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); |
185 | 191 | ||
186 | /* nfs3xdr.c */ | 192 | /* nfs3xdr.c */ |
187 | extern struct rpc_procinfo nfs3_procedures[]; | 193 | extern struct rpc_procinfo nfs3_procedures[]; |
188 | extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int); | 194 | extern __be32 *nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); |
189 | 195 | ||
190 | /* nfs4xdr.c */ | 196 | /* nfs4xdr.c */ |
191 | #ifdef CONFIG_NFS_V4 | 197 | #ifdef CONFIG_NFS_V4 |
192 | extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); | 198 | extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); |
193 | #endif | 199 | #endif |
194 | #ifdef CONFIG_NFS_V4_1 | 200 | #ifdef CONFIG_NFS_V4_1 |
195 | extern const u32 nfs41_maxread_overhead; | 201 | extern const u32 nfs41_maxread_overhead; |
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 59047f8d7d72..d610203d95c6 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c | |||
@@ -436,7 +436,7 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res) | |||
436 | 436 | ||
437 | for (i = 0; i < entries; i++) { | 437 | for (i = 0; i < entries; i++) { |
438 | flavors[i] = ntohl(*p++); | 438 | flavors[i] = ntohl(*p++); |
439 | dprintk("NFS:\tflavor %u: %d\n", i, flavors[i]); | 439 | dprintk("NFS: auth flavor[%u]: %d\n", i, flavors[i]); |
440 | } | 440 | } |
441 | *count = i; | 441 | *count = i; |
442 | 442 | ||
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index db8846a0e82e..e6bf45710cc7 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -337,10 +337,10 @@ nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args) | |||
337 | static int | 337 | static int |
338 | nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args) | 338 | nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args) |
339 | { | 339 | { |
340 | p = xdr_encode_fhandle(p, args->fromfh); | 340 | p = xdr_encode_fhandle(p, args->old_dir); |
341 | p = xdr_encode_array(p, args->fromname, args->fromlen); | 341 | p = xdr_encode_array(p, args->old_name->name, args->old_name->len); |
342 | p = xdr_encode_fhandle(p, args->tofh); | 342 | p = xdr_encode_fhandle(p, args->new_dir); |
343 | p = xdr_encode_array(p, args->toname, args->tolen); | 343 | p = xdr_encode_array(p, args->new_name->name, args->new_name->len); |
344 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 344 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); |
345 | return 0; | 345 | return 0; |
346 | } | 346 | } |
@@ -423,9 +423,7 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
423 | struct page **page; | 423 | struct page **page; |
424 | size_t hdrlen; | 424 | size_t hdrlen; |
425 | unsigned int pglen, recvd; | 425 | unsigned int pglen, recvd; |
426 | u32 len; | ||
427 | int status, nr = 0; | 426 | int status, nr = 0; |
428 | __be32 *end, *entry, *kaddr; | ||
429 | 427 | ||
430 | if ((status = ntohl(*p++))) | 428 | if ((status = ntohl(*p++))) |
431 | return nfs_stat_to_errno(status); | 429 | return nfs_stat_to_errno(status); |
@@ -445,80 +443,59 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
445 | if (pglen > recvd) | 443 | if (pglen > recvd) |
446 | pglen = recvd; | 444 | pglen = recvd; |
447 | page = rcvbuf->pages; | 445 | page = rcvbuf->pages; |
448 | kaddr = p = kmap_atomic(*page, KM_USER0); | ||
449 | end = (__be32 *)((char *)p + pglen); | ||
450 | entry = p; | ||
451 | |||
452 | /* Make sure the packet actually has a value_follows and EOF entry */ | ||
453 | if ((entry + 1) > end) | ||
454 | goto short_pkt; | ||
455 | |||
456 | for (; *p++; nr++) { | ||
457 | if (p + 2 > end) | ||
458 | goto short_pkt; | ||
459 | p++; /* fileid */ | ||
460 | len = ntohl(*p++); | ||
461 | p += XDR_QUADLEN(len) + 1; /* name plus cookie */ | ||
462 | if (len > NFS2_MAXNAMLEN) { | ||
463 | dprintk("NFS: giant filename in readdir (len 0x%x)!\n", | ||
464 | len); | ||
465 | goto err_unmap; | ||
466 | } | ||
467 | if (p + 2 > end) | ||
468 | goto short_pkt; | ||
469 | entry = p; | ||
470 | } | ||
471 | |||
472 | /* | ||
473 | * Apparently some server sends responses that are a valid size, but | ||
474 | * contain no entries, and have value_follows==0 and EOF==0. For | ||
475 | * those, just set the EOF marker. | ||
476 | */ | ||
477 | if (!nr && entry[1] == 0) { | ||
478 | dprintk("NFS: readdir reply truncated!\n"); | ||
479 | entry[1] = 1; | ||
480 | } | ||
481 | out: | ||
482 | kunmap_atomic(kaddr, KM_USER0); | ||
483 | return nr; | 446 | return nr; |
484 | short_pkt: | 447 | } |
485 | /* | 448 | |
486 | * When we get a short packet there are 2 possibilities. We can | 449 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) |
487 | * return an error, or fix up the response to look like a valid | 450 | { |
488 | * response and return what we have so far. If there are no | 451 | dprintk("nfs: %s: prematurely hit end of receive buffer. " |
489 | * entries and the packet was short, then return -EIO. If there | 452 | "Remaining buffer length is %tu words.\n", |
490 | * are valid entries in the response, return them and pretend that | 453 | func, xdr->end - xdr->p); |
491 | * the call was successful, but incomplete. The caller can retry the | ||
492 | * readdir starting at the last cookie. | ||
493 | */ | ||
494 | entry[0] = entry[1] = 0; | ||
495 | if (!nr) | ||
496 | nr = -errno_NFSERR_IO; | ||
497 | goto out; | ||
498 | err_unmap: | ||
499 | nr = -errno_NFSERR_IO; | ||
500 | goto out; | ||
501 | } | 454 | } |
502 | 455 | ||
503 | __be32 * | 456 | __be32 * |
504 | nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 457 | nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) |
505 | { | 458 | { |
506 | if (!*p++) { | 459 | __be32 *p; |
507 | if (!*p) | 460 | p = xdr_inline_decode(xdr, 4); |
461 | if (unlikely(!p)) | ||
462 | goto out_overflow; | ||
463 | if (!ntohl(*p++)) { | ||
464 | p = xdr_inline_decode(xdr, 4); | ||
465 | if (unlikely(!p)) | ||
466 | goto out_overflow; | ||
467 | if (!ntohl(*p++)) | ||
508 | return ERR_PTR(-EAGAIN); | 468 | return ERR_PTR(-EAGAIN); |
509 | entry->eof = 1; | 469 | entry->eof = 1; |
510 | return ERR_PTR(-EBADCOOKIE); | 470 | return ERR_PTR(-EBADCOOKIE); |
511 | } | 471 | } |
512 | 472 | ||
473 | p = xdr_inline_decode(xdr, 8); | ||
474 | if (unlikely(!p)) | ||
475 | goto out_overflow; | ||
476 | |||
513 | entry->ino = ntohl(*p++); | 477 | entry->ino = ntohl(*p++); |
514 | entry->len = ntohl(*p++); | 478 | entry->len = ntohl(*p++); |
479 | |||
480 | p = xdr_inline_decode(xdr, entry->len + 4); | ||
481 | if (unlikely(!p)) | ||
482 | goto out_overflow; | ||
515 | entry->name = (const char *) p; | 483 | entry->name = (const char *) p; |
516 | p += XDR_QUADLEN(entry->len); | 484 | p += XDR_QUADLEN(entry->len); |
517 | entry->prev_cookie = entry->cookie; | 485 | entry->prev_cookie = entry->cookie; |
518 | entry->cookie = ntohl(*p++); | 486 | entry->cookie = ntohl(*p++); |
519 | entry->eof = !p[0] && p[1]; | 487 | |
488 | p = xdr_inline_peek(xdr, 8); | ||
489 | if (p != NULL) | ||
490 | entry->eof = !p[0] && p[1]; | ||
491 | else | ||
492 | entry->eof = 0; | ||
520 | 493 | ||
521 | return p; | 494 | return p; |
495 | |||
496 | out_overflow: | ||
497 | print_overflow_msg(__func__, xdr); | ||
498 | return ERR_PTR(-EIO); | ||
522 | } | 499 | } |
523 | 500 | ||
524 | /* | 501 | /* |
@@ -596,7 +573,6 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
596 | struct kvec *iov = rcvbuf->head; | 573 | struct kvec *iov = rcvbuf->head; |
597 | size_t hdrlen; | 574 | size_t hdrlen; |
598 | u32 len, recvd; | 575 | u32 len, recvd; |
599 | char *kaddr; | ||
600 | int status; | 576 | int status; |
601 | 577 | ||
602 | if ((status = ntohl(*p++))) | 578 | if ((status = ntohl(*p++))) |
@@ -623,10 +599,7 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy) | |||
623 | return -EIO; | 599 | return -EIO; |
624 | } | 600 | } |
625 | 601 | ||
626 | /* NULL terminate the string we got */ | 602 | xdr_terminate_string(rcvbuf, len); |
627 | kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0); | ||
628 | kaddr[len+rcvbuf->page_base] = '\0'; | ||
629 | kunmap_atomic(kaddr, KM_USER0); | ||
630 | return 0; | 603 | return 0; |
631 | } | 604 | } |
632 | 605 | ||
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index fabb4f2849a1..ce939c062a52 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
@@ -313,7 +313,7 @@ static void nfs3_free_createdata(struct nfs3_createdata *data) | |||
313 | */ | 313 | */ |
314 | static int | 314 | static int |
315 | nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, | 315 | nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, |
316 | int flags, struct nameidata *nd) | 316 | int flags, struct nfs_open_context *ctx) |
317 | { | 317 | { |
318 | struct nfs3_createdata *data; | 318 | struct nfs3_createdata *data; |
319 | mode_t mode = sattr->ia_mode; | 319 | mode_t mode = sattr->ia_mode; |
@@ -438,19 +438,38 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir) | |||
438 | return 1; | 438 | return 1; |
439 | } | 439 | } |
440 | 440 | ||
441 | static void | ||
442 | nfs3_proc_rename_setup(struct rpc_message *msg, struct inode *dir) | ||
443 | { | ||
444 | msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME]; | ||
445 | } | ||
446 | |||
447 | static int | ||
448 | nfs3_proc_rename_done(struct rpc_task *task, struct inode *old_dir, | ||
449 | struct inode *new_dir) | ||
450 | { | ||
451 | struct nfs_renameres *res; | ||
452 | |||
453 | if (nfs3_async_handle_jukebox(task, old_dir)) | ||
454 | return 0; | ||
455 | res = task->tk_msg.rpc_resp; | ||
456 | |||
457 | nfs_post_op_update_inode(old_dir, res->old_fattr); | ||
458 | nfs_post_op_update_inode(new_dir, res->new_fattr); | ||
459 | return 1; | ||
460 | } | ||
461 | |||
441 | static int | 462 | static int |
442 | nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, | 463 | nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, |
443 | struct inode *new_dir, struct qstr *new_name) | 464 | struct inode *new_dir, struct qstr *new_name) |
444 | { | 465 | { |
445 | struct nfs3_renameargs arg = { | 466 | struct nfs_renameargs arg = { |
446 | .fromfh = NFS_FH(old_dir), | 467 | .old_dir = NFS_FH(old_dir), |
447 | .fromname = old_name->name, | 468 | .old_name = old_name, |
448 | .fromlen = old_name->len, | 469 | .new_dir = NFS_FH(new_dir), |
449 | .tofh = NFS_FH(new_dir), | 470 | .new_name = new_name, |
450 | .toname = new_name->name, | ||
451 | .tolen = new_name->len | ||
452 | }; | 471 | }; |
453 | struct nfs3_renameres res; | 472 | struct nfs_renameres res; |
454 | struct rpc_message msg = { | 473 | struct rpc_message msg = { |
455 | .rpc_proc = &nfs3_procedures[NFS3PROC_RENAME], | 474 | .rpc_proc = &nfs3_procedures[NFS3PROC_RENAME], |
456 | .rpc_argp = &arg, | 475 | .rpc_argp = &arg, |
@@ -460,17 +479,17 @@ nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, | |||
460 | 479 | ||
461 | dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); | 480 | dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); |
462 | 481 | ||
463 | res.fromattr = nfs_alloc_fattr(); | 482 | res.old_fattr = nfs_alloc_fattr(); |
464 | res.toattr = nfs_alloc_fattr(); | 483 | res.new_fattr = nfs_alloc_fattr(); |
465 | if (res.fromattr == NULL || res.toattr == NULL) | 484 | if (res.old_fattr == NULL || res.new_fattr == NULL) |
466 | goto out; | 485 | goto out; |
467 | 486 | ||
468 | status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); | 487 | status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); |
469 | nfs_post_op_update_inode(old_dir, res.fromattr); | 488 | nfs_post_op_update_inode(old_dir, res.old_fattr); |
470 | nfs_post_op_update_inode(new_dir, res.toattr); | 489 | nfs_post_op_update_inode(new_dir, res.new_fattr); |
471 | out: | 490 | out: |
472 | nfs_free_fattr(res.toattr); | 491 | nfs_free_fattr(res.old_fattr); |
473 | nfs_free_fattr(res.fromattr); | 492 | nfs_free_fattr(res.new_fattr); |
474 | dprintk("NFS reply rename: %d\n", status); | 493 | dprintk("NFS reply rename: %d\n", status); |
475 | return status; | 494 | return status; |
476 | } | 495 | } |
@@ -611,7 +630,7 @@ out: | |||
611 | */ | 630 | */ |
612 | static int | 631 | static int |
613 | nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 632 | nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
614 | u64 cookie, struct page *page, unsigned int count, int plus) | 633 | u64 cookie, struct page **pages, unsigned int count, int plus) |
615 | { | 634 | { |
616 | struct inode *dir = dentry->d_inode; | 635 | struct inode *dir = dentry->d_inode; |
617 | __be32 *verf = NFS_COOKIEVERF(dir); | 636 | __be32 *verf = NFS_COOKIEVERF(dir); |
@@ -621,7 +640,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
621 | .verf = {verf[0], verf[1]}, | 640 | .verf = {verf[0], verf[1]}, |
622 | .plus = plus, | 641 | .plus = plus, |
623 | .count = count, | 642 | .count = count, |
624 | .pages = &page | 643 | .pages = pages |
625 | }; | 644 | }; |
626 | struct nfs3_readdirres res = { | 645 | struct nfs3_readdirres res = { |
627 | .verf = verf, | 646 | .verf = verf, |
@@ -652,7 +671,8 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
652 | 671 | ||
653 | nfs_free_fattr(res.dir_attr); | 672 | nfs_free_fattr(res.dir_attr); |
654 | out: | 673 | out: |
655 | dprintk("NFS reply readdir: %d\n", status); | 674 | dprintk("NFS reply readdir%s: %d\n", |
675 | plus? "plus" : "", status); | ||
656 | return status; | 676 | return status; |
657 | } | 677 | } |
658 | 678 | ||
@@ -722,7 +742,7 @@ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, | |||
722 | dprintk("NFS call fsstat\n"); | 742 | dprintk("NFS call fsstat\n"); |
723 | nfs_fattr_init(stat->fattr); | 743 | nfs_fattr_init(stat->fattr); |
724 | status = rpc_call_sync(server->client, &msg, 0); | 744 | status = rpc_call_sync(server->client, &msg, 0); |
725 | dprintk("NFS reply statfs: %d\n", status); | 745 | dprintk("NFS reply fsstat: %d\n", status); |
726 | return status; | 746 | return status; |
727 | } | 747 | } |
728 | 748 | ||
@@ -844,6 +864,8 @@ const struct nfs_rpc_ops nfs_v3_clientops = { | |||
844 | .unlink_setup = nfs3_proc_unlink_setup, | 864 | .unlink_setup = nfs3_proc_unlink_setup, |
845 | .unlink_done = nfs3_proc_unlink_done, | 865 | .unlink_done = nfs3_proc_unlink_done, |
846 | .rename = nfs3_proc_rename, | 866 | .rename = nfs3_proc_rename, |
867 | .rename_setup = nfs3_proc_rename_setup, | ||
868 | .rename_done = nfs3_proc_rename_done, | ||
847 | .link = nfs3_proc_link, | 869 | .link = nfs3_proc_link, |
848 | .symlink = nfs3_proc_symlink, | 870 | .symlink = nfs3_proc_symlink, |
849 | .mkdir = nfs3_proc_mkdir, | 871 | .mkdir = nfs3_proc_mkdir, |
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 9769704f8ce6..d9a5e832c257 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c | |||
@@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = { | |||
100 | [NF3FIFO] = S_IFIFO, | 100 | [NF3FIFO] = S_IFIFO, |
101 | }; | 101 | }; |
102 | 102 | ||
103 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
104 | { | ||
105 | dprintk("nfs: %s: prematurely hit end of receive buffer. " | ||
106 | "Remaining buffer length is %tu words.\n", | ||
107 | func, xdr->end - xdr->p); | ||
108 | } | ||
109 | |||
103 | /* | 110 | /* |
104 | * Common NFS XDR functions as inlines | 111 | * Common NFS XDR functions as inlines |
105 | */ | 112 | */ |
@@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh) | |||
119 | return NULL; | 126 | return NULL; |
120 | } | 127 | } |
121 | 128 | ||
129 | static inline __be32 * | ||
130 | xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh) | ||
131 | { | ||
132 | __be32 *p; | ||
133 | p = xdr_inline_decode(xdr, 4); | ||
134 | if (unlikely(!p)) | ||
135 | goto out_overflow; | ||
136 | fh->size = ntohl(*p++); | ||
137 | |||
138 | if (fh->size <= NFS3_FHSIZE) { | ||
139 | p = xdr_inline_decode(xdr, fh->size); | ||
140 | if (unlikely(!p)) | ||
141 | goto out_overflow; | ||
142 | memcpy(fh->data, p, fh->size); | ||
143 | return p + XDR_QUADLEN(fh->size); | ||
144 | } | ||
145 | return NULL; | ||
146 | |||
147 | out_overflow: | ||
148 | print_overflow_msg(__func__, xdr); | ||
149 | return ERR_PTR(-EIO); | ||
150 | } | ||
151 | |||
122 | /* | 152 | /* |
123 | * Encode/decode time. | 153 | * Encode/decode time. |
124 | */ | 154 | */ |
@@ -241,6 +271,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr) | |||
241 | } | 271 | } |
242 | 272 | ||
243 | static inline __be32 * | 273 | static inline __be32 * |
274 | xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr) | ||
275 | { | ||
276 | __be32 *p; | ||
277 | |||
278 | p = xdr_inline_decode(xdr, 4); | ||
279 | if (unlikely(!p)) | ||
280 | goto out_overflow; | ||
281 | if (ntohl(*p++)) { | ||
282 | p = xdr_inline_decode(xdr, 84); | ||
283 | if (unlikely(!p)) | ||
284 | goto out_overflow; | ||
285 | p = xdr_decode_fattr(p, fattr); | ||
286 | } | ||
287 | return p; | ||
288 | out_overflow: | ||
289 | print_overflow_msg(__func__, xdr); | ||
290 | return ERR_PTR(-EIO); | ||
291 | } | ||
292 | |||
293 | static inline __be32 * | ||
244 | xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) | 294 | xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) |
245 | { | 295 | { |
246 | if (*p++) | 296 | if (*p++) |
@@ -442,12 +492,12 @@ nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args) | |||
442 | * Encode RENAME arguments | 492 | * Encode RENAME arguments |
443 | */ | 493 | */ |
444 | static int | 494 | static int |
445 | nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args) | 495 | nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args) |
446 | { | 496 | { |
447 | p = xdr_encode_fhandle(p, args->fromfh); | 497 | p = xdr_encode_fhandle(p, args->old_dir); |
448 | p = xdr_encode_array(p, args->fromname, args->fromlen); | 498 | p = xdr_encode_array(p, args->old_name->name, args->old_name->len); |
449 | p = xdr_encode_fhandle(p, args->tofh); | 499 | p = xdr_encode_fhandle(p, args->new_dir); |
450 | p = xdr_encode_array(p, args->toname, args->tolen); | 500 | p = xdr_encode_array(p, args->new_name->name, args->new_name->len); |
451 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 501 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); |
452 | return 0; | 502 | return 0; |
453 | } | 503 | } |
@@ -504,9 +554,8 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res | |||
504 | struct kvec *iov = rcvbuf->head; | 554 | struct kvec *iov = rcvbuf->head; |
505 | struct page **page; | 555 | struct page **page; |
506 | size_t hdrlen; | 556 | size_t hdrlen; |
507 | u32 len, recvd, pglen; | 557 | u32 recvd, pglen; |
508 | int status, nr = 0; | 558 | int status, nr = 0; |
509 | __be32 *entry, *end, *kaddr; | ||
510 | 559 | ||
511 | status = ntohl(*p++); | 560 | status = ntohl(*p++); |
512 | /* Decode post_op_attrs */ | 561 | /* Decode post_op_attrs */ |
@@ -536,99 +585,38 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res | |||
536 | if (pglen > recvd) | 585 | if (pglen > recvd) |
537 | pglen = recvd; | 586 | pglen = recvd; |
538 | page = rcvbuf->pages; | 587 | page = rcvbuf->pages; |
539 | kaddr = p = kmap_atomic(*page, KM_USER0); | ||
540 | end = (__be32 *)((char *)p + pglen); | ||
541 | entry = p; | ||
542 | |||
543 | /* Make sure the packet actually has a value_follows and EOF entry */ | ||
544 | if ((entry + 1) > end) | ||
545 | goto short_pkt; | ||
546 | |||
547 | for (; *p++; nr++) { | ||
548 | if (p + 3 > end) | ||
549 | goto short_pkt; | ||
550 | p += 2; /* inode # */ | ||
551 | len = ntohl(*p++); /* string length */ | ||
552 | p += XDR_QUADLEN(len) + 2; /* name + cookie */ | ||
553 | if (len > NFS3_MAXNAMLEN) { | ||
554 | dprintk("NFS: giant filename in readdir (len 0x%x)!\n", | ||
555 | len); | ||
556 | goto err_unmap; | ||
557 | } | ||
558 | 588 | ||
559 | if (res->plus) { | ||
560 | /* post_op_attr */ | ||
561 | if (p + 2 > end) | ||
562 | goto short_pkt; | ||
563 | if (*p++) { | ||
564 | p += 21; | ||
565 | if (p + 1 > end) | ||
566 | goto short_pkt; | ||
567 | } | ||
568 | /* post_op_fh3 */ | ||
569 | if (*p++) { | ||
570 | if (p + 1 > end) | ||
571 | goto short_pkt; | ||
572 | len = ntohl(*p++); | ||
573 | if (len > NFS3_FHSIZE) { | ||
574 | dprintk("NFS: giant filehandle in " | ||
575 | "readdir (len 0x%x)!\n", len); | ||
576 | goto err_unmap; | ||
577 | } | ||
578 | p += XDR_QUADLEN(len); | ||
579 | } | ||
580 | } | ||
581 | |||
582 | if (p + 2 > end) | ||
583 | goto short_pkt; | ||
584 | entry = p; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * Apparently some server sends responses that are a valid size, but | ||
589 | * contain no entries, and have value_follows==0 and EOF==0. For | ||
590 | * those, just set the EOF marker. | ||
591 | */ | ||
592 | if (!nr && entry[1] == 0) { | ||
593 | dprintk("NFS: readdir reply truncated!\n"); | ||
594 | entry[1] = 1; | ||
595 | } | ||
596 | out: | ||
597 | kunmap_atomic(kaddr, KM_USER0); | ||
598 | return nr; | 589 | return nr; |
599 | short_pkt: | ||
600 | /* | ||
601 | * When we get a short packet there are 2 possibilities. We can | ||
602 | * return an error, or fix up the response to look like a valid | ||
603 | * response and return what we have so far. If there are no | ||
604 | * entries and the packet was short, then return -EIO. If there | ||
605 | * are valid entries in the response, return them and pretend that | ||
606 | * the call was successful, but incomplete. The caller can retry the | ||
607 | * readdir starting at the last cookie. | ||
608 | */ | ||
609 | entry[0] = entry[1] = 0; | ||
610 | if (!nr) | ||
611 | nr = -errno_NFSERR_IO; | ||
612 | goto out; | ||
613 | err_unmap: | ||
614 | nr = -errno_NFSERR_IO; | ||
615 | goto out; | ||
616 | } | 590 | } |
617 | 591 | ||
618 | __be32 * | 592 | __be32 * |
619 | nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 593 | nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) |
620 | { | 594 | { |
595 | __be32 *p; | ||
621 | struct nfs_entry old = *entry; | 596 | struct nfs_entry old = *entry; |
622 | 597 | ||
623 | if (!*p++) { | 598 | p = xdr_inline_decode(xdr, 4); |
624 | if (!*p) | 599 | if (unlikely(!p)) |
600 | goto out_overflow; | ||
601 | if (!ntohl(*p++)) { | ||
602 | p = xdr_inline_decode(xdr, 4); | ||
603 | if (unlikely(!p)) | ||
604 | goto out_overflow; | ||
605 | if (!ntohl(*p++)) | ||
625 | return ERR_PTR(-EAGAIN); | 606 | return ERR_PTR(-EAGAIN); |
626 | entry->eof = 1; | 607 | entry->eof = 1; |
627 | return ERR_PTR(-EBADCOOKIE); | 608 | return ERR_PTR(-EBADCOOKIE); |
628 | } | 609 | } |
629 | 610 | ||
611 | p = xdr_inline_decode(xdr, 12); | ||
612 | if (unlikely(!p)) | ||
613 | goto out_overflow; | ||
630 | p = xdr_decode_hyper(p, &entry->ino); | 614 | p = xdr_decode_hyper(p, &entry->ino); |
631 | entry->len = ntohl(*p++); | 615 | entry->len = ntohl(*p++); |
616 | |||
617 | p = xdr_inline_decode(xdr, entry->len + 8); | ||
618 | if (unlikely(!p)) | ||
619 | goto out_overflow; | ||
632 | entry->name = (const char *) p; | 620 | entry->name = (const char *) p; |
633 | p += XDR_QUADLEN(entry->len); | 621 | p += XDR_QUADLEN(entry->len); |
634 | entry->prev_cookie = entry->cookie; | 622 | entry->prev_cookie = entry->cookie; |
@@ -636,10 +624,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | |||
636 | 624 | ||
637 | if (plus) { | 625 | if (plus) { |
638 | entry->fattr->valid = 0; | 626 | entry->fattr->valid = 0; |
639 | p = xdr_decode_post_op_attr(p, entry->fattr); | 627 | p = xdr_decode_post_op_attr_stream(xdr, entry->fattr); |
628 | if (IS_ERR(p)) | ||
629 | goto out_overflow_exit; | ||
640 | /* In fact, a post_op_fh3: */ | 630 | /* In fact, a post_op_fh3: */ |
631 | p = xdr_inline_decode(xdr, 4); | ||
632 | if (unlikely(!p)) | ||
633 | goto out_overflow; | ||
641 | if (*p++) { | 634 | if (*p++) { |
642 | p = xdr_decode_fhandle(p, entry->fh); | 635 | p = xdr_decode_fhandle_stream(xdr, entry->fh); |
636 | if (IS_ERR(p)) | ||
637 | goto out_overflow_exit; | ||
643 | /* Ugh -- server reply was truncated */ | 638 | /* Ugh -- server reply was truncated */ |
644 | if (p == NULL) { | 639 | if (p == NULL) { |
645 | dprintk("NFS: FH truncated\n"); | 640 | dprintk("NFS: FH truncated\n"); |
@@ -650,8 +645,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | |||
650 | memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); | 645 | memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); |
651 | } | 646 | } |
652 | 647 | ||
653 | entry->eof = !p[0] && p[1]; | 648 | p = xdr_inline_peek(xdr, 8); |
649 | if (p != NULL) | ||
650 | entry->eof = !p[0] && p[1]; | ||
651 | else | ||
652 | entry->eof = 0; | ||
653 | |||
654 | return p; | 654 | return p; |
655 | |||
656 | out_overflow: | ||
657 | print_overflow_msg(__func__, xdr); | ||
658 | out_overflow_exit: | ||
659 | return ERR_PTR(-EIO); | ||
655 | } | 660 | } |
656 | 661 | ||
657 | /* | 662 | /* |
@@ -824,7 +829,6 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) | |||
824 | struct kvec *iov = rcvbuf->head; | 829 | struct kvec *iov = rcvbuf->head; |
825 | size_t hdrlen; | 830 | size_t hdrlen; |
826 | u32 len, recvd; | 831 | u32 len, recvd; |
827 | char *kaddr; | ||
828 | int status; | 832 | int status; |
829 | 833 | ||
830 | status = ntohl(*p++); | 834 | status = ntohl(*p++); |
@@ -857,10 +861,7 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) | |||
857 | return -EIO; | 861 | return -EIO; |
858 | } | 862 | } |
859 | 863 | ||
860 | /* NULL terminate the string we got */ | 864 | xdr_terminate_string(rcvbuf, len); |
861 | kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0); | ||
862 | kaddr[len+rcvbuf->page_base] = '\0'; | ||
863 | kunmap_atomic(kaddr, KM_USER0); | ||
864 | return 0; | 865 | return 0; |
865 | } | 866 | } |
866 | 867 | ||
@@ -970,14 +971,14 @@ nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res) | |||
970 | * Decode RENAME reply | 971 | * Decode RENAME reply |
971 | */ | 972 | */ |
972 | static int | 973 | static int |
973 | nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res) | 974 | nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res) |
974 | { | 975 | { |
975 | int status; | 976 | int status; |
976 | 977 | ||
977 | if ((status = ntohl(*p++)) != 0) | 978 | if ((status = ntohl(*p++)) != 0) |
978 | status = nfs_stat_to_errno(status); | 979 | status = nfs_stat_to_errno(status); |
979 | p = xdr_decode_wcc_data(p, res->fromattr); | 980 | p = xdr_decode_wcc_data(p, res->old_fattr); |
980 | p = xdr_decode_wcc_data(p, res->toattr); | 981 | p = xdr_decode_wcc_data(p, res->new_fattr); |
981 | return status; | 982 | return status; |
982 | } | 983 | } |
983 | 984 | ||
@@ -1043,8 +1044,9 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res) | |||
1043 | res->wtmult = ntohl(*p++); | 1044 | res->wtmult = ntohl(*p++); |
1044 | res->dtpref = ntohl(*p++); | 1045 | res->dtpref = ntohl(*p++); |
1045 | p = xdr_decode_hyper(p, &res->maxfilesize); | 1046 | p = xdr_decode_hyper(p, &res->maxfilesize); |
1047 | p = xdr_decode_time3(p, &res->time_delta); | ||
1046 | 1048 | ||
1047 | /* ignore time_delta and properties */ | 1049 | /* ignore properties */ |
1048 | res->lease_time = 0; | 1050 | res->lease_time = 0; |
1049 | return 0; | 1051 | return 0; |
1050 | } | 1052 | } |
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 311e15cc8af0..9fa496387fdf 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -242,8 +242,6 @@ extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); | |||
242 | extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); | 242 | extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); |
243 | extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); | 243 | extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); |
244 | extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); | 244 | extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); |
245 | extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); | ||
246 | extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); | ||
247 | extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); | 245 | extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); |
248 | extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, | 246 | extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, |
249 | struct nfs4_fs_locations *fs_locations, struct page *page); | 247 | struct nfs4_fs_locations *fs_locations, struct page *page); |
@@ -333,7 +331,7 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid); | |||
333 | extern const nfs4_stateid zero_stateid; | 331 | extern const nfs4_stateid zero_stateid; |
334 | 332 | ||
335 | /* nfs4xdr.c */ | 333 | /* nfs4xdr.c */ |
336 | extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); | 334 | extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); |
337 | extern struct rpc_procinfo nfs4_procedures[]; | 335 | extern struct rpc_procinfo nfs4_procedures[]; |
338 | 336 | ||
339 | struct nfs4_mount_data; | 337 | struct nfs4_mount_data; |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 089da5b5d20a..e87fe612ca18 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -129,7 +129,7 @@ const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE | |||
129 | | FATTR4_WORD0_MAXREAD | 129 | | FATTR4_WORD0_MAXREAD |
130 | | FATTR4_WORD0_MAXWRITE | 130 | | FATTR4_WORD0_MAXWRITE |
131 | | FATTR4_WORD0_LEASE_TIME, | 131 | | FATTR4_WORD0_LEASE_TIME, |
132 | 0 | 132 | FATTR4_WORD1_TIME_DELTA |
133 | }; | 133 | }; |
134 | 134 | ||
135 | const u32 nfs4_fs_locations_bitmap[2] = { | 135 | const u32 nfs4_fs_locations_bitmap[2] = { |
@@ -255,9 +255,6 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, | |||
255 | nfs4_state_mark_reclaim_nograce(clp, state); | 255 | nfs4_state_mark_reclaim_nograce(clp, state); |
256 | goto do_state_recovery; | 256 | goto do_state_recovery; |
257 | case -NFS4ERR_STALE_STATEID: | 257 | case -NFS4ERR_STALE_STATEID: |
258 | if (state == NULL) | ||
259 | break; | ||
260 | nfs4_state_mark_reclaim_reboot(clp, state); | ||
261 | case -NFS4ERR_STALE_CLIENTID: | 258 | case -NFS4ERR_STALE_CLIENTID: |
262 | case -NFS4ERR_EXPIRED: | 259 | case -NFS4ERR_EXPIRED: |
263 | goto do_state_recovery; | 260 | goto do_state_recovery; |
@@ -334,10 +331,12 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp | |||
334 | * Must be called while holding tbl->slot_tbl_lock | 331 | * Must be called while holding tbl->slot_tbl_lock |
335 | */ | 332 | */ |
336 | static void | 333 | static void |
337 | nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) | 334 | nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *free_slot) |
338 | { | 335 | { |
336 | int free_slotid = free_slot - tbl->slots; | ||
339 | int slotid = free_slotid; | 337 | int slotid = free_slotid; |
340 | 338 | ||
339 | BUG_ON(slotid < 0 || slotid >= NFS4_MAX_SLOT_TABLE); | ||
341 | /* clear used bit in bitmap */ | 340 | /* clear used bit in bitmap */ |
342 | __clear_bit(slotid, tbl->used_slots); | 341 | __clear_bit(slotid, tbl->used_slots); |
343 | 342 | ||
@@ -379,7 +378,7 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) | |||
379 | struct nfs4_slot_table *tbl; | 378 | struct nfs4_slot_table *tbl; |
380 | 379 | ||
381 | tbl = &res->sr_session->fc_slot_table; | 380 | tbl = &res->sr_session->fc_slot_table; |
382 | if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) { | 381 | if (!res->sr_slot) { |
383 | /* just wake up the next guy waiting since | 382 | /* just wake up the next guy waiting since |
384 | * we may have not consumed a slot after all */ | 383 | * we may have not consumed a slot after all */ |
385 | dprintk("%s: No slot\n", __func__); | 384 | dprintk("%s: No slot\n", __func__); |
@@ -387,17 +386,15 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) | |||
387 | } | 386 | } |
388 | 387 | ||
389 | spin_lock(&tbl->slot_tbl_lock); | 388 | spin_lock(&tbl->slot_tbl_lock); |
390 | nfs4_free_slot(tbl, res->sr_slotid); | 389 | nfs4_free_slot(tbl, res->sr_slot); |
391 | nfs41_check_drain_session_complete(res->sr_session); | 390 | nfs41_check_drain_session_complete(res->sr_session); |
392 | spin_unlock(&tbl->slot_tbl_lock); | 391 | spin_unlock(&tbl->slot_tbl_lock); |
393 | res->sr_slotid = NFS4_MAX_SLOT_TABLE; | 392 | res->sr_slot = NULL; |
394 | } | 393 | } |
395 | 394 | ||
396 | static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) | 395 | static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) |
397 | { | 396 | { |
398 | unsigned long timestamp; | 397 | unsigned long timestamp; |
399 | struct nfs4_slot_table *tbl; | ||
400 | struct nfs4_slot *slot; | ||
401 | struct nfs_client *clp; | 398 | struct nfs_client *clp; |
402 | 399 | ||
403 | /* | 400 | /* |
@@ -410,17 +407,14 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * | |||
410 | res->sr_status = NFS_OK; | 407 | res->sr_status = NFS_OK; |
411 | 408 | ||
412 | /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */ | 409 | /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */ |
413 | if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) | 410 | if (!res->sr_slot) |
414 | goto out; | 411 | goto out; |
415 | 412 | ||
416 | tbl = &res->sr_session->fc_slot_table; | ||
417 | slot = tbl->slots + res->sr_slotid; | ||
418 | |||
419 | /* Check the SEQUENCE operation status */ | 413 | /* Check the SEQUENCE operation status */ |
420 | switch (res->sr_status) { | 414 | switch (res->sr_status) { |
421 | case 0: | 415 | case 0: |
422 | /* Update the slot's sequence and clientid lease timer */ | 416 | /* Update the slot's sequence and clientid lease timer */ |
423 | ++slot->seq_nr; | 417 | ++res->sr_slot->seq_nr; |
424 | timestamp = res->sr_renewal_time; | 418 | timestamp = res->sr_renewal_time; |
425 | clp = res->sr_session->clp; | 419 | clp = res->sr_session->clp; |
426 | do_renew_lease(clp, timestamp); | 420 | do_renew_lease(clp, timestamp); |
@@ -433,12 +427,14 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * | |||
433 | * returned NFS4ERR_DELAY as per Section 2.10.6.2 | 427 | * returned NFS4ERR_DELAY as per Section 2.10.6.2 |
434 | * of RFC5661. | 428 | * of RFC5661. |
435 | */ | 429 | */ |
436 | dprintk("%s: slot=%d seq=%d: Operation in progress\n", | 430 | dprintk("%s: slot=%ld seq=%d: Operation in progress\n", |
437 | __func__, res->sr_slotid, slot->seq_nr); | 431 | __func__, |
432 | res->sr_slot - res->sr_session->fc_slot_table.slots, | ||
433 | res->sr_slot->seq_nr); | ||
438 | goto out_retry; | 434 | goto out_retry; |
439 | default: | 435 | default: |
440 | /* Just update the slot sequence no. */ | 436 | /* Just update the slot sequence no. */ |
441 | ++slot->seq_nr; | 437 | ++res->sr_slot->seq_nr; |
442 | } | 438 | } |
443 | out: | 439 | out: |
444 | /* The session may be reset by one of the error handlers. */ | 440 | /* The session may be reset by one of the error handlers. */ |
@@ -505,10 +501,9 @@ static int nfs41_setup_sequence(struct nfs4_session *session, | |||
505 | 501 | ||
506 | dprintk("--> %s\n", __func__); | 502 | dprintk("--> %s\n", __func__); |
507 | /* slot already allocated? */ | 503 | /* slot already allocated? */ |
508 | if (res->sr_slotid != NFS4_MAX_SLOT_TABLE) | 504 | if (res->sr_slot != NULL) |
509 | return 0; | 505 | return 0; |
510 | 506 | ||
511 | res->sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
512 | tbl = &session->fc_slot_table; | 507 | tbl = &session->fc_slot_table; |
513 | 508 | ||
514 | spin_lock(&tbl->slot_tbl_lock); | 509 | spin_lock(&tbl->slot_tbl_lock); |
@@ -550,7 +545,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session, | |||
550 | dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); | 545 | dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); |
551 | 546 | ||
552 | res->sr_session = session; | 547 | res->sr_session = session; |
553 | res->sr_slotid = slotid; | 548 | res->sr_slot = slot; |
554 | res->sr_renewal_time = jiffies; | 549 | res->sr_renewal_time = jiffies; |
555 | res->sr_status_flags = 0; | 550 | res->sr_status_flags = 0; |
556 | /* | 551 | /* |
@@ -576,8 +571,9 @@ int nfs4_setup_sequence(const struct nfs_server *server, | |||
576 | goto out; | 571 | goto out; |
577 | } | 572 | } |
578 | 573 | ||
579 | dprintk("--> %s clp %p session %p sr_slotid %d\n", | 574 | dprintk("--> %s clp %p session %p sr_slot %ld\n", |
580 | __func__, session->clp, session, res->sr_slotid); | 575 | __func__, session->clp, session, res->sr_slot ? |
576 | res->sr_slot - session->fc_slot_table.slots : -1); | ||
581 | 577 | ||
582 | ret = nfs41_setup_sequence(session, args, res, cache_reply, | 578 | ret = nfs41_setup_sequence(session, args, res, cache_reply, |
583 | task); | 579 | task); |
@@ -650,7 +646,7 @@ static int nfs4_call_sync_sequence(struct nfs_server *server, | |||
650 | .callback_data = &data | 646 | .callback_data = &data |
651 | }; | 647 | }; |
652 | 648 | ||
653 | res->sr_slotid = NFS4_MAX_SLOT_TABLE; | 649 | res->sr_slot = NULL; |
654 | if (privileged) | 650 | if (privileged) |
655 | task_setup.callback_ops = &nfs41_call_priv_sync_ops; | 651 | task_setup.callback_ops = &nfs41_call_priv_sync_ops; |
656 | task = rpc_run_task(&task_setup); | 652 | task = rpc_run_task(&task_setup); |
@@ -735,7 +731,6 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p) | |||
735 | p->o_res.server = p->o_arg.server; | 731 | p->o_res.server = p->o_arg.server; |
736 | nfs_fattr_init(&p->f_attr); | 732 | nfs_fattr_init(&p->f_attr); |
737 | nfs_fattr_init(&p->dir_attr); | 733 | nfs_fattr_init(&p->dir_attr); |
738 | p->o_res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
739 | } | 734 | } |
740 | 735 | ||
741 | static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, | 736 | static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, |
@@ -1120,6 +1115,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state * | |||
1120 | clear_bit(NFS_DELEGATED_STATE, &state->flags); | 1115 | clear_bit(NFS_DELEGATED_STATE, &state->flags); |
1121 | smp_rmb(); | 1116 | smp_rmb(); |
1122 | if (state->n_rdwr != 0) { | 1117 | if (state->n_rdwr != 0) { |
1118 | clear_bit(NFS_O_RDWR_STATE, &state->flags); | ||
1123 | ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate); | 1119 | ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate); |
1124 | if (ret != 0) | 1120 | if (ret != 0) |
1125 | return ret; | 1121 | return ret; |
@@ -1127,6 +1123,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state * | |||
1127 | return -ESTALE; | 1123 | return -ESTALE; |
1128 | } | 1124 | } |
1129 | if (state->n_wronly != 0) { | 1125 | if (state->n_wronly != 0) { |
1126 | clear_bit(NFS_O_WRONLY_STATE, &state->flags); | ||
1130 | ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate); | 1127 | ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate); |
1131 | if (ret != 0) | 1128 | if (ret != 0) |
1132 | return ret; | 1129 | return ret; |
@@ -1134,6 +1131,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state * | |||
1134 | return -ESTALE; | 1131 | return -ESTALE; |
1135 | } | 1132 | } |
1136 | if (state->n_rdonly != 0) { | 1133 | if (state->n_rdonly != 0) { |
1134 | clear_bit(NFS_O_RDONLY_STATE, &state->flags); | ||
1137 | ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate); | 1135 | ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate); |
1138 | if (ret != 0) | 1136 | if (ret != 0) |
1139 | return ret; | 1137 | return ret; |
@@ -1188,7 +1186,7 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state | |||
1188 | int err; | 1186 | int err; |
1189 | do { | 1187 | do { |
1190 | err = _nfs4_do_open_reclaim(ctx, state); | 1188 | err = _nfs4_do_open_reclaim(ctx, state); |
1191 | if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED) | 1189 | if (err != -NFS4ERR_DELAY) |
1192 | break; | 1190 | break; |
1193 | nfs4_handle_exception(server, err, &exception); | 1191 | nfs4_handle_exception(server, err, &exception); |
1194 | } while (exception.retry); | 1192 | } while (exception.retry); |
@@ -1258,6 +1256,13 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state | |||
1258 | case -NFS4ERR_ADMIN_REVOKED: | 1256 | case -NFS4ERR_ADMIN_REVOKED: |
1259 | case -NFS4ERR_BAD_STATEID: | 1257 | case -NFS4ERR_BAD_STATEID: |
1260 | nfs4_state_mark_reclaim_nograce(server->nfs_client, state); | 1258 | nfs4_state_mark_reclaim_nograce(server->nfs_client, state); |
1259 | case -EKEYEXPIRED: | ||
1260 | /* | ||
1261 | * User RPCSEC_GSS context has expired. | ||
1262 | * We cannot recover this stateid now, so | ||
1263 | * skip it and allow recovery thread to | ||
1264 | * proceed. | ||
1265 | */ | ||
1261 | case -ENOMEM: | 1266 | case -ENOMEM: |
1262 | err = 0; | 1267 | err = 0; |
1263 | goto out; | 1268 | goto out; |
@@ -1605,7 +1610,6 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state | |||
1605 | goto out; | 1610 | goto out; |
1606 | case -NFS4ERR_GRACE: | 1611 | case -NFS4ERR_GRACE: |
1607 | case -NFS4ERR_DELAY: | 1612 | case -NFS4ERR_DELAY: |
1608 | case -EKEYEXPIRED: | ||
1609 | nfs4_handle_exception(server, err, &exception); | 1613 | nfs4_handle_exception(server, err, &exception); |
1610 | err = 0; | 1614 | err = 0; |
1611 | } | 1615 | } |
@@ -1975,7 +1979,6 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, i | |||
1975 | calldata->res.fattr = &calldata->fattr; | 1979 | calldata->res.fattr = &calldata->fattr; |
1976 | calldata->res.seqid = calldata->arg.seqid; | 1980 | calldata->res.seqid = calldata->arg.seqid; |
1977 | calldata->res.server = server; | 1981 | calldata->res.server = server; |
1978 | calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
1979 | path_get(path); | 1982 | path_get(path); |
1980 | calldata->path = *path; | 1983 | calldata->path = *path; |
1981 | 1984 | ||
@@ -1998,120 +2001,17 @@ out: | |||
1998 | return status; | 2001 | return status; |
1999 | } | 2002 | } |
2000 | 2003 | ||
2001 | static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode) | 2004 | static struct inode * |
2002 | { | 2005 | nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr) |
2003 | struct file *filp; | ||
2004 | int ret; | ||
2005 | |||
2006 | /* If the open_intent is for execute, we have an extra check to make */ | ||
2007 | if (fmode & FMODE_EXEC) { | ||
2008 | ret = nfs_may_open(state->inode, | ||
2009 | state->owner->so_cred, | ||
2010 | nd->intent.open.flags); | ||
2011 | if (ret < 0) | ||
2012 | goto out_close; | ||
2013 | } | ||
2014 | filp = lookup_instantiate_filp(nd, path->dentry, NULL); | ||
2015 | if (!IS_ERR(filp)) { | ||
2016 | struct nfs_open_context *ctx; | ||
2017 | ctx = nfs_file_open_context(filp); | ||
2018 | ctx->state = state; | ||
2019 | return 0; | ||
2020 | } | ||
2021 | ret = PTR_ERR(filp); | ||
2022 | out_close: | ||
2023 | nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE)); | ||
2024 | return ret; | ||
2025 | } | ||
2026 | |||
2027 | struct dentry * | ||
2028 | nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | ||
2029 | { | 2006 | { |
2030 | struct path path = { | ||
2031 | .mnt = nd->path.mnt, | ||
2032 | .dentry = dentry, | ||
2033 | }; | ||
2034 | struct dentry *parent; | ||
2035 | struct iattr attr; | ||
2036 | struct rpc_cred *cred; | ||
2037 | struct nfs4_state *state; | 2007 | struct nfs4_state *state; |
2038 | struct dentry *res; | ||
2039 | int open_flags = nd->intent.open.flags; | ||
2040 | fmode_t fmode = open_flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC); | ||
2041 | |||
2042 | if (nd->flags & LOOKUP_CREATE) { | ||
2043 | attr.ia_mode = nd->intent.open.create_mode; | ||
2044 | attr.ia_valid = ATTR_MODE; | ||
2045 | if (!IS_POSIXACL(dir)) | ||
2046 | attr.ia_mode &= ~current_umask(); | ||
2047 | } else { | ||
2048 | open_flags &= ~O_EXCL; | ||
2049 | attr.ia_valid = 0; | ||
2050 | BUG_ON(open_flags & O_CREAT); | ||
2051 | } | ||
2052 | 2008 | ||
2053 | cred = rpc_lookup_cred(); | ||
2054 | if (IS_ERR(cred)) | ||
2055 | return (struct dentry *)cred; | ||
2056 | parent = dentry->d_parent; | ||
2057 | /* Protect against concurrent sillydeletes */ | 2009 | /* Protect against concurrent sillydeletes */ |
2058 | nfs_block_sillyrename(parent); | 2010 | state = nfs4_do_open(dir, &ctx->path, ctx->mode, open_flags, attr, ctx->cred); |
2059 | state = nfs4_do_open(dir, &path, fmode, open_flags, &attr, cred); | 2011 | if (IS_ERR(state)) |
2060 | put_rpccred(cred); | 2012 | return ERR_CAST(state); |
2061 | if (IS_ERR(state)) { | 2013 | ctx->state = state; |
2062 | if (PTR_ERR(state) == -ENOENT) { | 2014 | return igrab(state->inode); |
2063 | d_add(dentry, NULL); | ||
2064 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
2065 | } | ||
2066 | nfs_unblock_sillyrename(parent); | ||
2067 | return (struct dentry *)state; | ||
2068 | } | ||
2069 | res = d_add_unique(dentry, igrab(state->inode)); | ||
2070 | if (res != NULL) | ||
2071 | path.dentry = res; | ||
2072 | nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir)); | ||
2073 | nfs_unblock_sillyrename(parent); | ||
2074 | nfs4_intent_set_file(nd, &path, state, fmode); | ||
2075 | return res; | ||
2076 | } | ||
2077 | |||
2078 | int | ||
2079 | nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd) | ||
2080 | { | ||
2081 | struct path path = { | ||
2082 | .mnt = nd->path.mnt, | ||
2083 | .dentry = dentry, | ||
2084 | }; | ||
2085 | struct rpc_cred *cred; | ||
2086 | struct nfs4_state *state; | ||
2087 | fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE); | ||
2088 | |||
2089 | cred = rpc_lookup_cred(); | ||
2090 | if (IS_ERR(cred)) | ||
2091 | return PTR_ERR(cred); | ||
2092 | state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred); | ||
2093 | put_rpccred(cred); | ||
2094 | if (IS_ERR(state)) { | ||
2095 | switch (PTR_ERR(state)) { | ||
2096 | case -EPERM: | ||
2097 | case -EACCES: | ||
2098 | case -EDQUOT: | ||
2099 | case -ENOSPC: | ||
2100 | case -EROFS: | ||
2101 | return PTR_ERR(state); | ||
2102 | default: | ||
2103 | goto out_drop; | ||
2104 | } | ||
2105 | } | ||
2106 | if (state->inode == dentry->d_inode) { | ||
2107 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | ||
2108 | nfs4_intent_set_file(nd, &path, state, fmode); | ||
2109 | return 1; | ||
2110 | } | ||
2111 | nfs4_close_sync(&path, state, fmode); | ||
2112 | out_drop: | ||
2113 | d_drop(dentry); | ||
2114 | return 0; | ||
2115 | } | 2015 | } |
2116 | 2016 | ||
2117 | static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) | 2017 | static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) |
@@ -2568,36 +2468,34 @@ static int nfs4_proc_readlink(struct inode *inode, struct page *page, | |||
2568 | 2468 | ||
2569 | static int | 2469 | static int |
2570 | nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, | 2470 | nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, |
2571 | int flags, struct nameidata *nd) | 2471 | int flags, struct nfs_open_context *ctx) |
2572 | { | 2472 | { |
2573 | struct path path = { | 2473 | struct path my_path = { |
2574 | .mnt = nd->path.mnt, | ||
2575 | .dentry = dentry, | 2474 | .dentry = dentry, |
2576 | }; | 2475 | }; |
2476 | struct path *path = &my_path; | ||
2577 | struct nfs4_state *state; | 2477 | struct nfs4_state *state; |
2578 | struct rpc_cred *cred; | 2478 | struct rpc_cred *cred = NULL; |
2579 | fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE); | 2479 | fmode_t fmode = 0; |
2580 | int status = 0; | 2480 | int status = 0; |
2581 | 2481 | ||
2582 | cred = rpc_lookup_cred(); | 2482 | if (ctx != NULL) { |
2583 | if (IS_ERR(cred)) { | 2483 | cred = ctx->cred; |
2584 | status = PTR_ERR(cred); | 2484 | path = &ctx->path; |
2585 | goto out; | 2485 | fmode = ctx->mode; |
2586 | } | 2486 | } |
2587 | state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred); | 2487 | state = nfs4_do_open(dir, path, fmode, flags, sattr, cred); |
2588 | d_drop(dentry); | 2488 | d_drop(dentry); |
2589 | if (IS_ERR(state)) { | 2489 | if (IS_ERR(state)) { |
2590 | status = PTR_ERR(state); | 2490 | status = PTR_ERR(state); |
2591 | goto out_putcred; | 2491 | goto out; |
2592 | } | 2492 | } |
2593 | d_add(dentry, igrab(state->inode)); | 2493 | d_add(dentry, igrab(state->inode)); |
2594 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | 2494 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
2595 | if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0) | 2495 | if (ctx != NULL) |
2596 | status = nfs4_intent_set_file(nd, &path, state, fmode); | 2496 | ctx->state = state; |
2597 | else | 2497 | else |
2598 | nfs4_close_sync(&path, state, fmode); | 2498 | nfs4_close_sync(path, state, fmode); |
2599 | out_putcred: | ||
2600 | put_rpccred(cred); | ||
2601 | out: | 2499 | out: |
2602 | return status; | 2500 | return status; |
2603 | } | 2501 | } |
@@ -2655,6 +2553,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) | |||
2655 | 2553 | ||
2656 | args->bitmask = server->cache_consistency_bitmask; | 2554 | args->bitmask = server->cache_consistency_bitmask; |
2657 | res->server = server; | 2555 | res->server = server; |
2556 | res->seq_res.sr_slot = NULL; | ||
2658 | msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; | 2557 | msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; |
2659 | } | 2558 | } |
2660 | 2559 | ||
@@ -2671,18 +2570,46 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) | |||
2671 | return 1; | 2570 | return 1; |
2672 | } | 2571 | } |
2673 | 2572 | ||
2573 | static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir) | ||
2574 | { | ||
2575 | struct nfs_server *server = NFS_SERVER(dir); | ||
2576 | struct nfs_renameargs *arg = msg->rpc_argp; | ||
2577 | struct nfs_renameres *res = msg->rpc_resp; | ||
2578 | |||
2579 | msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME]; | ||
2580 | arg->bitmask = server->attr_bitmask; | ||
2581 | res->server = server; | ||
2582 | } | ||
2583 | |||
2584 | static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir, | ||
2585 | struct inode *new_dir) | ||
2586 | { | ||
2587 | struct nfs_renameres *res = task->tk_msg.rpc_resp; | ||
2588 | |||
2589 | if (!nfs4_sequence_done(task, &res->seq_res)) | ||
2590 | return 0; | ||
2591 | if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) | ||
2592 | return 0; | ||
2593 | |||
2594 | update_changeattr(old_dir, &res->old_cinfo); | ||
2595 | nfs_post_op_update_inode(old_dir, res->old_fattr); | ||
2596 | update_changeattr(new_dir, &res->new_cinfo); | ||
2597 | nfs_post_op_update_inode(new_dir, res->new_fattr); | ||
2598 | return 1; | ||
2599 | } | ||
2600 | |||
2674 | static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, | 2601 | static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, |
2675 | struct inode *new_dir, struct qstr *new_name) | 2602 | struct inode *new_dir, struct qstr *new_name) |
2676 | { | 2603 | { |
2677 | struct nfs_server *server = NFS_SERVER(old_dir); | 2604 | struct nfs_server *server = NFS_SERVER(old_dir); |
2678 | struct nfs4_rename_arg arg = { | 2605 | struct nfs_renameargs arg = { |
2679 | .old_dir = NFS_FH(old_dir), | 2606 | .old_dir = NFS_FH(old_dir), |
2680 | .new_dir = NFS_FH(new_dir), | 2607 | .new_dir = NFS_FH(new_dir), |
2681 | .old_name = old_name, | 2608 | .old_name = old_name, |
2682 | .new_name = new_name, | 2609 | .new_name = new_name, |
2683 | .bitmask = server->attr_bitmask, | 2610 | .bitmask = server->attr_bitmask, |
2684 | }; | 2611 | }; |
2685 | struct nfs4_rename_res res = { | 2612 | struct nfs_renameres res = { |
2686 | .server = server, | 2613 | .server = server, |
2687 | }; | 2614 | }; |
2688 | struct rpc_message msg = { | 2615 | struct rpc_message msg = { |
@@ -2896,15 +2823,16 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, | |||
2896 | } | 2823 | } |
2897 | 2824 | ||
2898 | static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 2825 | static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
2899 | u64 cookie, struct page *page, unsigned int count, int plus) | 2826 | u64 cookie, struct page **pages, unsigned int count, int plus) |
2900 | { | 2827 | { |
2901 | struct inode *dir = dentry->d_inode; | 2828 | struct inode *dir = dentry->d_inode; |
2902 | struct nfs4_readdir_arg args = { | 2829 | struct nfs4_readdir_arg args = { |
2903 | .fh = NFS_FH(dir), | 2830 | .fh = NFS_FH(dir), |
2904 | .pages = &page, | 2831 | .pages = pages, |
2905 | .pgbase = 0, | 2832 | .pgbase = 0, |
2906 | .count = count, | 2833 | .count = count, |
2907 | .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask, | 2834 | .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask, |
2835 | .plus = plus, | ||
2908 | }; | 2836 | }; |
2909 | struct nfs4_readdir_res res; | 2837 | struct nfs4_readdir_res res; |
2910 | struct rpc_message msg = { | 2838 | struct rpc_message msg = { |
@@ -2932,14 +2860,14 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
2932 | } | 2860 | } |
2933 | 2861 | ||
2934 | static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 2862 | static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
2935 | u64 cookie, struct page *page, unsigned int count, int plus) | 2863 | u64 cookie, struct page **pages, unsigned int count, int plus) |
2936 | { | 2864 | { |
2937 | struct nfs4_exception exception = { }; | 2865 | struct nfs4_exception exception = { }; |
2938 | int err; | 2866 | int err; |
2939 | do { | 2867 | do { |
2940 | err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode), | 2868 | err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode), |
2941 | _nfs4_proc_readdir(dentry, cred, cookie, | 2869 | _nfs4_proc_readdir(dentry, cred, cookie, |
2942 | page, count, plus), | 2870 | pages, count, plus), |
2943 | &exception); | 2871 | &exception); |
2944 | } while (exception.retry); | 2872 | } while (exception.retry); |
2945 | return err; | 2873 | return err; |
@@ -3490,9 +3418,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, | |||
3490 | nfs4_state_mark_reclaim_nograce(clp, state); | 3418 | nfs4_state_mark_reclaim_nograce(clp, state); |
3491 | goto do_state_recovery; | 3419 | goto do_state_recovery; |
3492 | case -NFS4ERR_STALE_STATEID: | 3420 | case -NFS4ERR_STALE_STATEID: |
3493 | if (state == NULL) | ||
3494 | break; | ||
3495 | nfs4_state_mark_reclaim_reboot(clp, state); | ||
3496 | case -NFS4ERR_STALE_CLIENTID: | 3421 | case -NFS4ERR_STALE_CLIENTID: |
3497 | case -NFS4ERR_EXPIRED: | 3422 | case -NFS4ERR_EXPIRED: |
3498 | goto do_state_recovery; | 3423 | goto do_state_recovery; |
@@ -3626,7 +3551,6 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp, | |||
3626 | case -NFS4ERR_RESOURCE: | 3551 | case -NFS4ERR_RESOURCE: |
3627 | /* The IBM lawyers misread another document! */ | 3552 | /* The IBM lawyers misread another document! */ |
3628 | case -NFS4ERR_DELAY: | 3553 | case -NFS4ERR_DELAY: |
3629 | case -EKEYEXPIRED: | ||
3630 | err = nfs4_delay(clp->cl_rpcclient, &timeout); | 3554 | err = nfs4_delay(clp->cl_rpcclient, &timeout); |
3631 | } | 3555 | } |
3632 | } while (err == 0); | 3556 | } while (err == 0); |
@@ -3721,7 +3645,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co | |||
3721 | memcpy(&data->stateid, stateid, sizeof(data->stateid)); | 3645 | memcpy(&data->stateid, stateid, sizeof(data->stateid)); |
3722 | data->res.fattr = &data->fattr; | 3646 | data->res.fattr = &data->fattr; |
3723 | data->res.server = server; | 3647 | data->res.server = server; |
3724 | data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
3725 | nfs_fattr_init(data->res.fattr); | 3648 | nfs_fattr_init(data->res.fattr); |
3726 | data->timestamp = jiffies; | 3649 | data->timestamp = jiffies; |
3727 | data->rpc_status = 0; | 3650 | data->rpc_status = 0; |
@@ -3874,7 +3797,6 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl, | |||
3874 | p->arg.fl = &p->fl; | 3797 | p->arg.fl = &p->fl; |
3875 | p->arg.seqid = seqid; | 3798 | p->arg.seqid = seqid; |
3876 | p->res.seqid = seqid; | 3799 | p->res.seqid = seqid; |
3877 | p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
3878 | p->arg.stateid = &lsp->ls_stateid; | 3800 | p->arg.stateid = &lsp->ls_stateid; |
3879 | p->lsp = lsp; | 3801 | p->lsp = lsp; |
3880 | atomic_inc(&lsp->ls_count); | 3802 | atomic_inc(&lsp->ls_count); |
@@ -4054,7 +3976,6 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, | |||
4054 | p->arg.lock_owner.clientid = server->nfs_client->cl_clientid; | 3976 | p->arg.lock_owner.clientid = server->nfs_client->cl_clientid; |
4055 | p->arg.lock_owner.id = lsp->ls_id.id; | 3977 | p->arg.lock_owner.id = lsp->ls_id.id; |
4056 | p->res.lock_seqid = p->arg.lock_seqid; | 3978 | p->res.lock_seqid = p->arg.lock_seqid; |
4057 | p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
4058 | p->lsp = lsp; | 3979 | p->lsp = lsp; |
4059 | p->server = server; | 3980 | p->server = server; |
4060 | atomic_inc(&lsp->ls_count); | 3981 | atomic_inc(&lsp->ls_count); |
@@ -4241,7 +4162,7 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request | |||
4241 | if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) | 4162 | if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) |
4242 | return 0; | 4163 | return 0; |
4243 | err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM); | 4164 | err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM); |
4244 | if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED) | 4165 | if (err != -NFS4ERR_DELAY) |
4245 | break; | 4166 | break; |
4246 | nfs4_handle_exception(server, err, &exception); | 4167 | nfs4_handle_exception(server, err, &exception); |
4247 | } while (exception.retry); | 4168 | } while (exception.retry); |
@@ -4266,7 +4187,6 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request | |||
4266 | goto out; | 4187 | goto out; |
4267 | case -NFS4ERR_GRACE: | 4188 | case -NFS4ERR_GRACE: |
4268 | case -NFS4ERR_DELAY: | 4189 | case -NFS4ERR_DELAY: |
4269 | case -EKEYEXPIRED: | ||
4270 | nfs4_handle_exception(server, err, &exception); | 4190 | nfs4_handle_exception(server, err, &exception); |
4271 | err = 0; | 4191 | err = 0; |
4272 | } | 4192 | } |
@@ -4412,13 +4332,21 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) | |||
4412 | nfs4_state_mark_reclaim_nograce(server->nfs_client, state); | 4332 | nfs4_state_mark_reclaim_nograce(server->nfs_client, state); |
4413 | err = 0; | 4333 | err = 0; |
4414 | goto out; | 4334 | goto out; |
4335 | case -EKEYEXPIRED: | ||
4336 | /* | ||
4337 | * User RPCSEC_GSS context has expired. | ||
4338 | * We cannot recover this stateid now, so | ||
4339 | * skip it and allow recovery thread to | ||
4340 | * proceed. | ||
4341 | */ | ||
4342 | err = 0; | ||
4343 | goto out; | ||
4415 | case -ENOMEM: | 4344 | case -ENOMEM: |
4416 | case -NFS4ERR_DENIED: | 4345 | case -NFS4ERR_DENIED: |
4417 | /* kill_proc(fl->fl_pid, SIGLOST, 1); */ | 4346 | /* kill_proc(fl->fl_pid, SIGLOST, 1); */ |
4418 | err = 0; | 4347 | err = 0; |
4419 | goto out; | 4348 | goto out; |
4420 | case -NFS4ERR_DELAY: | 4349 | case -NFS4ERR_DELAY: |
4421 | case -EKEYEXPIRED: | ||
4422 | break; | 4350 | break; |
4423 | } | 4351 | } |
4424 | err = nfs4_handle_exception(server, err, &exception); | 4352 | err = nfs4_handle_exception(server, err, &exception); |
@@ -4647,7 +4575,6 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata) | |||
4647 | switch (task->tk_status) { | 4575 | switch (task->tk_status) { |
4648 | case -NFS4ERR_DELAY: | 4576 | case -NFS4ERR_DELAY: |
4649 | case -NFS4ERR_GRACE: | 4577 | case -NFS4ERR_GRACE: |
4650 | case -EKEYEXPIRED: | ||
4651 | dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status); | 4578 | dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status); |
4652 | rpc_delay(task, NFS4_POLL_RETRY_MIN); | 4579 | rpc_delay(task, NFS4_POLL_RETRY_MIN); |
4653 | task->tk_status = 0; | 4580 | task->tk_status = 0; |
@@ -4687,7 +4614,6 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) | |||
4687 | }; | 4614 | }; |
4688 | int status; | 4615 | int status; |
4689 | 4616 | ||
4690 | res.lr_seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
4691 | dprintk("--> %s\n", __func__); | 4617 | dprintk("--> %s\n", __func__); |
4692 | task = rpc_run_task(&task_setup); | 4618 | task = rpc_run_task(&task_setup); |
4693 | 4619 | ||
@@ -5111,7 +5037,6 @@ static int nfs41_sequence_handle_errors(struct rpc_task *task, struct nfs_client | |||
5111 | { | 5037 | { |
5112 | switch(task->tk_status) { | 5038 | switch(task->tk_status) { |
5113 | case -NFS4ERR_DELAY: | 5039 | case -NFS4ERR_DELAY: |
5114 | case -EKEYEXPIRED: | ||
5115 | rpc_delay(task, NFS4_POLL_RETRY_MAX); | 5040 | rpc_delay(task, NFS4_POLL_RETRY_MAX); |
5116 | return -EAGAIN; | 5041 | return -EAGAIN; |
5117 | default: | 5042 | default: |
@@ -5180,12 +5105,11 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_ | |||
5180 | 5105 | ||
5181 | if (!atomic_inc_not_zero(&clp->cl_count)) | 5106 | if (!atomic_inc_not_zero(&clp->cl_count)) |
5182 | return ERR_PTR(-EIO); | 5107 | return ERR_PTR(-EIO); |
5183 | calldata = kmalloc(sizeof(*calldata), GFP_NOFS); | 5108 | calldata = kzalloc(sizeof(*calldata), GFP_NOFS); |
5184 | if (calldata == NULL) { | 5109 | if (calldata == NULL) { |
5185 | nfs_put_client(clp); | 5110 | nfs_put_client(clp); |
5186 | return ERR_PTR(-ENOMEM); | 5111 | return ERR_PTR(-ENOMEM); |
5187 | } | 5112 | } |
5188 | calldata->res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
5189 | msg.rpc_argp = &calldata->args; | 5113 | msg.rpc_argp = &calldata->args; |
5190 | msg.rpc_resp = &calldata->res; | 5114 | msg.rpc_resp = &calldata->res; |
5191 | calldata->clp = clp; | 5115 | calldata->clp = clp; |
@@ -5254,7 +5178,6 @@ static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nf | |||
5254 | case -NFS4ERR_WRONG_CRED: /* What to do here? */ | 5178 | case -NFS4ERR_WRONG_CRED: /* What to do here? */ |
5255 | break; | 5179 | break; |
5256 | case -NFS4ERR_DELAY: | 5180 | case -NFS4ERR_DELAY: |
5257 | case -EKEYEXPIRED: | ||
5258 | rpc_delay(task, NFS4_POLL_RETRY_MAX); | 5181 | rpc_delay(task, NFS4_POLL_RETRY_MAX); |
5259 | return -EAGAIN; | 5182 | return -EAGAIN; |
5260 | default: | 5183 | default: |
@@ -5317,7 +5240,6 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp) | |||
5317 | goto out; | 5240 | goto out; |
5318 | calldata->clp = clp; | 5241 | calldata->clp = clp; |
5319 | calldata->arg.one_fs = 0; | 5242 | calldata->arg.one_fs = 0; |
5320 | calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
5321 | 5243 | ||
5322 | msg.rpc_argp = &calldata->arg; | 5244 | msg.rpc_argp = &calldata->arg; |
5323 | msg.rpc_resp = &calldata->res; | 5245 | msg.rpc_resp = &calldata->res; |
@@ -5443,6 +5365,8 @@ const struct nfs_rpc_ops nfs_v4_clientops = { | |||
5443 | .unlink_setup = nfs4_proc_unlink_setup, | 5365 | .unlink_setup = nfs4_proc_unlink_setup, |
5444 | .unlink_done = nfs4_proc_unlink_done, | 5366 | .unlink_done = nfs4_proc_unlink_done, |
5445 | .rename = nfs4_proc_rename, | 5367 | .rename = nfs4_proc_rename, |
5368 | .rename_setup = nfs4_proc_rename_setup, | ||
5369 | .rename_done = nfs4_proc_rename_done, | ||
5446 | .link = nfs4_proc_link, | 5370 | .link = nfs4_proc_link, |
5447 | .symlink = nfs4_proc_symlink, | 5371 | .symlink = nfs4_proc_symlink, |
5448 | .mkdir = nfs4_proc_mkdir, | 5372 | .mkdir = nfs4_proc_mkdir, |
@@ -5463,6 +5387,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { | |||
5463 | .lock = nfs4_proc_lock, | 5387 | .lock = nfs4_proc_lock, |
5464 | .clear_acl_cache = nfs4_zap_acl_attr, | 5388 | .clear_acl_cache = nfs4_zap_acl_attr, |
5465 | .close_context = nfs4_close_context, | 5389 | .close_context = nfs4_close_context, |
5390 | .open_context = nfs4_atomic_open, | ||
5466 | }; | 5391 | }; |
5467 | 5392 | ||
5468 | /* | 5393 | /* |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 96524c5dca6b..aa0b02a610c4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <linux/kthread.h> | 46 | #include <linux/kthread.h> |
47 | #include <linux/module.h> | 47 | #include <linux/module.h> |
48 | #include <linux/random.h> | 48 | #include <linux/random.h> |
49 | #include <linux/ratelimit.h> | ||
49 | #include <linux/workqueue.h> | 50 | #include <linux/workqueue.h> |
50 | #include <linux/bitops.h> | 51 | #include <linux/bitops.h> |
51 | 52 | ||
@@ -1063,6 +1064,14 @@ restart: | |||
1063 | /* Mark the file as being 'closed' */ | 1064 | /* Mark the file as being 'closed' */ |
1064 | state->state = 0; | 1065 | state->state = 0; |
1065 | break; | 1066 | break; |
1067 | case -EKEYEXPIRED: | ||
1068 | /* | ||
1069 | * User RPCSEC_GSS context has expired. | ||
1070 | * We cannot recover this stateid now, so | ||
1071 | * skip it and allow recovery thread to | ||
1072 | * proceed. | ||
1073 | */ | ||
1074 | break; | ||
1066 | case -NFS4ERR_ADMIN_REVOKED: | 1075 | case -NFS4ERR_ADMIN_REVOKED: |
1067 | case -NFS4ERR_STALE_STATEID: | 1076 | case -NFS4ERR_STALE_STATEID: |
1068 | case -NFS4ERR_BAD_STATEID: | 1077 | case -NFS4ERR_BAD_STATEID: |
@@ -1138,16 +1147,14 @@ static void nfs4_reclaim_complete(struct nfs_client *clp, | |||
1138 | (void)ops->reclaim_complete(clp); | 1147 | (void)ops->reclaim_complete(clp); |
1139 | } | 1148 | } |
1140 | 1149 | ||
1141 | static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) | 1150 | static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp) |
1142 | { | 1151 | { |
1143 | struct nfs4_state_owner *sp; | 1152 | struct nfs4_state_owner *sp; |
1144 | struct rb_node *pos; | 1153 | struct rb_node *pos; |
1145 | struct nfs4_state *state; | 1154 | struct nfs4_state *state; |
1146 | 1155 | ||
1147 | if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) | 1156 | if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) |
1148 | return; | 1157 | return 0; |
1149 | |||
1150 | nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops); | ||
1151 | 1158 | ||
1152 | for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { | 1159 | for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { |
1153 | sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); | 1160 | sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); |
@@ -1161,6 +1168,14 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) | |||
1161 | } | 1168 | } |
1162 | 1169 | ||
1163 | nfs_delegation_reap_unclaimed(clp); | 1170 | nfs_delegation_reap_unclaimed(clp); |
1171 | return 1; | ||
1172 | } | ||
1173 | |||
1174 | static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) | ||
1175 | { | ||
1176 | if (!nfs4_state_clear_reclaim_reboot(clp)) | ||
1177 | return; | ||
1178 | nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops); | ||
1164 | } | 1179 | } |
1165 | 1180 | ||
1166 | static void nfs_delegation_clear_all(struct nfs_client *clp) | 1181 | static void nfs_delegation_clear_all(struct nfs_client *clp) |
@@ -1175,6 +1190,14 @@ static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) | |||
1175 | nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); | 1190 | nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); |
1176 | } | 1191 | } |
1177 | 1192 | ||
1193 | static void nfs4_warn_keyexpired(const char *s) | ||
1194 | { | ||
1195 | printk_ratelimited(KERN_WARNING "Error: state manager" | ||
1196 | " encountered RPCSEC_GSS session" | ||
1197 | " expired against NFSv4 server %s.\n", | ||
1198 | s); | ||
1199 | } | ||
1200 | |||
1178 | static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) | 1201 | static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) |
1179 | { | 1202 | { |
1180 | switch (error) { | 1203 | switch (error) { |
@@ -1187,7 +1210,7 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) | |||
1187 | case -NFS4ERR_STALE_CLIENTID: | 1210 | case -NFS4ERR_STALE_CLIENTID: |
1188 | case -NFS4ERR_LEASE_MOVED: | 1211 | case -NFS4ERR_LEASE_MOVED: |
1189 | set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); | 1212 | set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); |
1190 | nfs4_state_end_reclaim_reboot(clp); | 1213 | nfs4_state_clear_reclaim_reboot(clp); |
1191 | nfs4_state_start_reclaim_reboot(clp); | 1214 | nfs4_state_start_reclaim_reboot(clp); |
1192 | break; | 1215 | break; |
1193 | case -NFS4ERR_EXPIRED: | 1216 | case -NFS4ERR_EXPIRED: |
@@ -1204,6 +1227,10 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) | |||
1204 | set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); | 1227 | set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); |
1205 | /* Zero session reset errors */ | 1228 | /* Zero session reset errors */ |
1206 | return 0; | 1229 | return 0; |
1230 | case -EKEYEXPIRED: | ||
1231 | /* Nothing we can do */ | ||
1232 | nfs4_warn_keyexpired(clp->cl_hostname); | ||
1233 | return 0; | ||
1207 | } | 1234 | } |
1208 | return error; | 1235 | return error; |
1209 | } | 1236 | } |
@@ -1414,9 +1441,10 @@ static void nfs4_set_lease_expired(struct nfs_client *clp, int status) | |||
1414 | case -NFS4ERR_DELAY: | 1441 | case -NFS4ERR_DELAY: |
1415 | case -NFS4ERR_CLID_INUSE: | 1442 | case -NFS4ERR_CLID_INUSE: |
1416 | case -EAGAIN: | 1443 | case -EAGAIN: |
1417 | case -EKEYEXPIRED: | ||
1418 | break; | 1444 | break; |
1419 | 1445 | ||
1446 | case -EKEYEXPIRED: | ||
1447 | nfs4_warn_keyexpired(clp->cl_hostname); | ||
1420 | case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery | 1448 | case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery |
1421 | * in nfs4_exchange_id */ | 1449 | * in nfs4_exchange_id */ |
1422 | default: | 1450 | default: |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 08ef91291132..bd2101d918c8 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -816,7 +816,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const | |||
816 | if (iap->ia_valid & ATTR_MODE) | 816 | if (iap->ia_valid & ATTR_MODE) |
817 | len += 4; | 817 | len += 4; |
818 | if (iap->ia_valid & ATTR_UID) { | 818 | if (iap->ia_valid & ATTR_UID) { |
819 | owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name); | 819 | owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name, IDMAP_NAMESZ); |
820 | if (owner_namelen < 0) { | 820 | if (owner_namelen < 0) { |
821 | dprintk("nfs: couldn't resolve uid %d to string\n", | 821 | dprintk("nfs: couldn't resolve uid %d to string\n", |
822 | iap->ia_uid); | 822 | iap->ia_uid); |
@@ -828,7 +828,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const | |||
828 | len += 4 + (XDR_QUADLEN(owner_namelen) << 2); | 828 | len += 4 + (XDR_QUADLEN(owner_namelen) << 2); |
829 | } | 829 | } |
830 | if (iap->ia_valid & ATTR_GID) { | 830 | if (iap->ia_valid & ATTR_GID) { |
831 | owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group); | 831 | owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group, IDMAP_NAMESZ); |
832 | if (owner_grouplen < 0) { | 832 | if (owner_grouplen < 0) { |
833 | dprintk("nfs: couldn't resolve gid %d to string\n", | 833 | dprintk("nfs: couldn't resolve gid %d to string\n", |
834 | iap->ia_gid); | 834 | iap->ia_gid); |
@@ -1385,24 +1385,35 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, | |||
1385 | 1385 | ||
1386 | static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr) | 1386 | static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr) |
1387 | { | 1387 | { |
1388 | uint32_t attrs[2] = { | 1388 | uint32_t attrs[2] = {0, 0}; |
1389 | FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID, | 1389 | uint32_t dircount = readdir->count >> 1; |
1390 | FATTR4_WORD1_MOUNTED_ON_FILEID, | ||
1391 | }; | ||
1392 | __be32 *p; | 1390 | __be32 *p; |
1393 | 1391 | ||
1392 | if (readdir->plus) { | ||
1393 | attrs[0] |= FATTR4_WORD0_TYPE|FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE| | ||
1394 | FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE; | ||
1395 | attrs[1] |= FATTR4_WORD1_MODE|FATTR4_WORD1_NUMLINKS|FATTR4_WORD1_OWNER| | ||
1396 | FATTR4_WORD1_OWNER_GROUP|FATTR4_WORD1_RAWDEV| | ||
1397 | FATTR4_WORD1_SPACE_USED|FATTR4_WORD1_TIME_ACCESS| | ||
1398 | FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; | ||
1399 | dircount >>= 1; | ||
1400 | } | ||
1401 | attrs[0] |= FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID; | ||
1402 | attrs[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID; | ||
1403 | /* Switch to mounted_on_fileid if the server supports it */ | ||
1404 | if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) | ||
1405 | attrs[0] &= ~FATTR4_WORD0_FILEID; | ||
1406 | else | ||
1407 | attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; | ||
1408 | |||
1394 | p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20); | 1409 | p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20); |
1395 | *p++ = cpu_to_be32(OP_READDIR); | 1410 | *p++ = cpu_to_be32(OP_READDIR); |
1396 | p = xdr_encode_hyper(p, readdir->cookie); | 1411 | p = xdr_encode_hyper(p, readdir->cookie); |
1397 | p = xdr_encode_opaque_fixed(p, readdir->verifier.data, NFS4_VERIFIER_SIZE); | 1412 | p = xdr_encode_opaque_fixed(p, readdir->verifier.data, NFS4_VERIFIER_SIZE); |
1398 | *p++ = cpu_to_be32(readdir->count >> 1); /* We're not doing readdirplus */ | 1413 | *p++ = cpu_to_be32(dircount); |
1399 | *p++ = cpu_to_be32(readdir->count); | 1414 | *p++ = cpu_to_be32(readdir->count); |
1400 | *p++ = cpu_to_be32(2); | 1415 | *p++ = cpu_to_be32(2); |
1401 | /* Switch to mounted_on_fileid if the server supports it */ | 1416 | |
1402 | if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) | ||
1403 | attrs[0] &= ~FATTR4_WORD0_FILEID; | ||
1404 | else | ||
1405 | attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; | ||
1406 | *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]); | 1417 | *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]); |
1407 | *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]); | 1418 | *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]); |
1408 | hdr->nops++; | 1419 | hdr->nops++; |
@@ -1823,7 +1834,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs | |||
1823 | /* | 1834 | /* |
1824 | * Encode RENAME request | 1835 | * Encode RENAME request |
1825 | */ | 1836 | */ |
1826 | static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs4_rename_arg *args) | 1837 | static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs_renameargs *args) |
1827 | { | 1838 | { |
1828 | struct xdr_stream xdr; | 1839 | struct xdr_stream xdr; |
1829 | struct compound_hdr hdr = { | 1840 | struct compound_hdr hdr = { |
@@ -2676,7 +2687,10 @@ out_overflow: | |||
2676 | static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask) | 2687 | static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask) |
2677 | { | 2688 | { |
2678 | if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) { | 2689 | if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) { |
2679 | decode_attr_bitmap(xdr, bitmask); | 2690 | int ret; |
2691 | ret = decode_attr_bitmap(xdr, bitmask); | ||
2692 | if (unlikely(ret < 0)) | ||
2693 | return ret; | ||
2680 | bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS; | 2694 | bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS; |
2681 | } else | 2695 | } else |
2682 | bitmask[0] = bitmask[1] = 0; | 2696 | bitmask[0] = bitmask[1] = 0; |
@@ -2848,6 +2862,56 @@ out_overflow: | |||
2848 | return -EIO; | 2862 | return -EIO; |
2849 | } | 2863 | } |
2850 | 2864 | ||
2865 | static int decode_attr_error(struct xdr_stream *xdr, uint32_t *bitmap) | ||
2866 | { | ||
2867 | __be32 *p; | ||
2868 | |||
2869 | if (unlikely(bitmap[0] & (FATTR4_WORD0_RDATTR_ERROR - 1U))) | ||
2870 | return -EIO; | ||
2871 | if (likely(bitmap[0] & FATTR4_WORD0_RDATTR_ERROR)) { | ||
2872 | p = xdr_inline_decode(xdr, 4); | ||
2873 | if (unlikely(!p)) | ||
2874 | goto out_overflow; | ||
2875 | bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; | ||
2876 | } | ||
2877 | return 0; | ||
2878 | out_overflow: | ||
2879 | print_overflow_msg(__func__, xdr); | ||
2880 | return -EIO; | ||
2881 | } | ||
2882 | |||
2883 | static int decode_attr_filehandle(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fh *fh) | ||
2884 | { | ||
2885 | __be32 *p; | ||
2886 | int len; | ||
2887 | |||
2888 | if (fh != NULL) | ||
2889 | memset(fh, 0, sizeof(*fh)); | ||
2890 | |||
2891 | if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEHANDLE - 1U))) | ||
2892 | return -EIO; | ||
2893 | if (likely(bitmap[0] & FATTR4_WORD0_FILEHANDLE)) { | ||
2894 | p = xdr_inline_decode(xdr, 4); | ||
2895 | if (unlikely(!p)) | ||
2896 | goto out_overflow; | ||
2897 | len = be32_to_cpup(p); | ||
2898 | if (len > NFS4_FHSIZE) | ||
2899 | return -EIO; | ||
2900 | p = xdr_inline_decode(xdr, len); | ||
2901 | if (unlikely(!p)) | ||
2902 | goto out_overflow; | ||
2903 | if (fh != NULL) { | ||
2904 | memcpy(fh->data, p, len); | ||
2905 | fh->size = len; | ||
2906 | } | ||
2907 | bitmap[0] &= ~FATTR4_WORD0_FILEHANDLE; | ||
2908 | } | ||
2909 | return 0; | ||
2910 | out_overflow: | ||
2911 | print_overflow_msg(__func__, xdr); | ||
2912 | return -EIO; | ||
2913 | } | ||
2914 | |||
2851 | static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) | 2915 | static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res) |
2852 | { | 2916 | { |
2853 | __be32 *p; | 2917 | __be32 *p; |
@@ -3521,6 +3585,24 @@ static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, s | |||
3521 | return status; | 3585 | return status; |
3522 | } | 3586 | } |
3523 | 3587 | ||
3588 | static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap, | ||
3589 | struct timespec *time) | ||
3590 | { | ||
3591 | int status = 0; | ||
3592 | |||
3593 | time->tv_sec = 0; | ||
3594 | time->tv_nsec = 0; | ||
3595 | if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_DELTA - 1U))) | ||
3596 | return -EIO; | ||
3597 | if (likely(bitmap[1] & FATTR4_WORD1_TIME_DELTA)) { | ||
3598 | status = decode_attr_time(xdr, time); | ||
3599 | bitmap[1] &= ~FATTR4_WORD1_TIME_DELTA; | ||
3600 | } | ||
3601 | dprintk("%s: time_delta=%ld %ld\n", __func__, (long)time->tv_sec, | ||
3602 | (long)time->tv_nsec); | ||
3603 | return status; | ||
3604 | } | ||
3605 | |||
3524 | static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) | 3606 | static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) |
3525 | { | 3607 | { |
3526 | int status = 0; | 3608 | int status = 0; |
@@ -3744,29 +3826,14 @@ xdr_error: | |||
3744 | return status; | 3826 | return status; |
3745 | } | 3827 | } |
3746 | 3828 | ||
3747 | static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, | 3829 | static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, |
3830 | struct nfs_fattr *fattr, struct nfs_fh *fh, | ||
3748 | const struct nfs_server *server, int may_sleep) | 3831 | const struct nfs_server *server, int may_sleep) |
3749 | { | 3832 | { |
3750 | __be32 *savep; | ||
3751 | uint32_t attrlen, | ||
3752 | bitmap[2] = {0}, | ||
3753 | type; | ||
3754 | int status; | 3833 | int status; |
3755 | umode_t fmode = 0; | 3834 | umode_t fmode = 0; |
3756 | uint64_t fileid; | 3835 | uint64_t fileid; |
3757 | 3836 | uint32_t type; | |
3758 | status = decode_op_hdr(xdr, OP_GETATTR); | ||
3759 | if (status < 0) | ||
3760 | goto xdr_error; | ||
3761 | |||
3762 | status = decode_attr_bitmap(xdr, bitmap); | ||
3763 | if (status < 0) | ||
3764 | goto xdr_error; | ||
3765 | |||
3766 | status = decode_attr_length(xdr, &attrlen, &savep); | ||
3767 | if (status < 0) | ||
3768 | goto xdr_error; | ||
3769 | |||
3770 | 3837 | ||
3771 | status = decode_attr_type(xdr, bitmap, &type); | 3838 | status = decode_attr_type(xdr, bitmap, &type); |
3772 | if (status < 0) | 3839 | if (status < 0) |
@@ -3792,6 +3859,14 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, | |||
3792 | goto xdr_error; | 3859 | goto xdr_error; |
3793 | fattr->valid |= status; | 3860 | fattr->valid |= status; |
3794 | 3861 | ||
3862 | status = decode_attr_error(xdr, bitmap); | ||
3863 | if (status < 0) | ||
3864 | goto xdr_error; | ||
3865 | |||
3866 | status = decode_attr_filehandle(xdr, bitmap, fh); | ||
3867 | if (status < 0) | ||
3868 | goto xdr_error; | ||
3869 | |||
3795 | status = decode_attr_fileid(xdr, bitmap, &fattr->fileid); | 3870 | status = decode_attr_fileid(xdr, bitmap, &fattr->fileid); |
3796 | if (status < 0) | 3871 | if (status < 0) |
3797 | goto xdr_error; | 3872 | goto xdr_error; |
@@ -3862,12 +3937,46 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, | |||
3862 | fattr->valid |= status; | 3937 | fattr->valid |= status; |
3863 | } | 3938 | } |
3864 | 3939 | ||
3940 | xdr_error: | ||
3941 | dprintk("%s: xdr returned %d\n", __func__, -status); | ||
3942 | return status; | ||
3943 | } | ||
3944 | |||
3945 | static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr, | ||
3946 | struct nfs_fh *fh, const struct nfs_server *server, int may_sleep) | ||
3947 | { | ||
3948 | __be32 *savep; | ||
3949 | uint32_t attrlen, | ||
3950 | bitmap[2] = {0}; | ||
3951 | int status; | ||
3952 | |||
3953 | status = decode_op_hdr(xdr, OP_GETATTR); | ||
3954 | if (status < 0) | ||
3955 | goto xdr_error; | ||
3956 | |||
3957 | status = decode_attr_bitmap(xdr, bitmap); | ||
3958 | if (status < 0) | ||
3959 | goto xdr_error; | ||
3960 | |||
3961 | status = decode_attr_length(xdr, &attrlen, &savep); | ||
3962 | if (status < 0) | ||
3963 | goto xdr_error; | ||
3964 | |||
3965 | status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, server, may_sleep); | ||
3966 | if (status < 0) | ||
3967 | goto xdr_error; | ||
3968 | |||
3865 | status = verify_attr_len(xdr, savep, attrlen); | 3969 | status = verify_attr_len(xdr, savep, attrlen); |
3866 | xdr_error: | 3970 | xdr_error: |
3867 | dprintk("%s: xdr returned %d\n", __func__, -status); | 3971 | dprintk("%s: xdr returned %d\n", __func__, -status); |
3868 | return status; | 3972 | return status; |
3869 | } | 3973 | } |
3870 | 3974 | ||
3975 | static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, | ||
3976 | const struct nfs_server *server, int may_sleep) | ||
3977 | { | ||
3978 | return decode_getfattr_generic(xdr, fattr, NULL, server, may_sleep); | ||
3979 | } | ||
3871 | 3980 | ||
3872 | static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) | 3981 | static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) |
3873 | { | 3982 | { |
@@ -3894,6 +4003,9 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) | |||
3894 | if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0) | 4003 | if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0) |
3895 | goto xdr_error; | 4004 | goto xdr_error; |
3896 | fsinfo->wtpref = fsinfo->wtmax; | 4005 | fsinfo->wtpref = fsinfo->wtmax; |
4006 | status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta); | ||
4007 | if (status != 0) | ||
4008 | goto xdr_error; | ||
3897 | 4009 | ||
3898 | status = verify_attr_len(xdr, savep, attrlen); | 4010 | status = verify_attr_len(xdr, savep, attrlen); |
3899 | xdr_error: | 4011 | xdr_error: |
@@ -3950,13 +4062,13 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) | |||
3950 | __be32 *p; | 4062 | __be32 *p; |
3951 | uint32_t namelen, type; | 4063 | uint32_t namelen, type; |
3952 | 4064 | ||
3953 | p = xdr_inline_decode(xdr, 32); | 4065 | p = xdr_inline_decode(xdr, 32); /* read 32 bytes */ |
3954 | if (unlikely(!p)) | 4066 | if (unlikely(!p)) |
3955 | goto out_overflow; | 4067 | goto out_overflow; |
3956 | p = xdr_decode_hyper(p, &offset); | 4068 | p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */ |
3957 | p = xdr_decode_hyper(p, &length); | 4069 | p = xdr_decode_hyper(p, &length); |
3958 | type = be32_to_cpup(p++); | 4070 | type = be32_to_cpup(p++); /* 4 byte read */ |
3959 | if (fl != NULL) { | 4071 | if (fl != NULL) { /* manipulate file lock */ |
3960 | fl->fl_start = (loff_t)offset; | 4072 | fl->fl_start = (loff_t)offset; |
3961 | fl->fl_end = fl->fl_start + (loff_t)length - 1; | 4073 | fl->fl_end = fl->fl_start + (loff_t)length - 1; |
3962 | if (length == ~(uint64_t)0) | 4074 | if (length == ~(uint64_t)0) |
@@ -3966,9 +4078,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) | |||
3966 | fl->fl_type = F_RDLCK; | 4078 | fl->fl_type = F_RDLCK; |
3967 | fl->fl_pid = 0; | 4079 | fl->fl_pid = 0; |
3968 | } | 4080 | } |
3969 | p = xdr_decode_hyper(p, &clientid); | 4081 | p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */ |
3970 | namelen = be32_to_cpup(p); | 4082 | namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */ |
3971 | p = xdr_inline_decode(xdr, namelen); | 4083 | p = xdr_inline_decode(xdr, namelen); /* variable size field */ |
3972 | if (likely(p)) | 4084 | if (likely(p)) |
3973 | return -NFS4ERR_DENIED; | 4085 | return -NFS4ERR_DENIED; |
3974 | out_overflow: | 4086 | out_overflow: |
@@ -4200,12 +4312,9 @@ out_overflow: | |||
4200 | static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) | 4312 | static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) |
4201 | { | 4313 | { |
4202 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | 4314 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; |
4203 | struct page *page = *rcvbuf->pages; | ||
4204 | struct kvec *iov = rcvbuf->head; | 4315 | struct kvec *iov = rcvbuf->head; |
4205 | size_t hdrlen; | 4316 | size_t hdrlen; |
4206 | u32 recvd, pglen = rcvbuf->page_len; | 4317 | u32 recvd, pglen = rcvbuf->page_len; |
4207 | __be32 *end, *entry, *p, *kaddr; | ||
4208 | unsigned int nr = 0; | ||
4209 | int status; | 4318 | int status; |
4210 | 4319 | ||
4211 | status = decode_op_hdr(xdr, OP_READDIR); | 4320 | status = decode_op_hdr(xdr, OP_READDIR); |
@@ -4225,71 +4334,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n | |||
4225 | pglen = recvd; | 4334 | pglen = recvd; |
4226 | xdr_read_pages(xdr, pglen); | 4335 | xdr_read_pages(xdr, pglen); |
4227 | 4336 | ||
4228 | BUG_ON(pglen + readdir->pgbase > PAGE_CACHE_SIZE); | 4337 | |
4229 | kaddr = p = kmap_atomic(page, KM_USER0); | ||
4230 | end = p + ((pglen + readdir->pgbase) >> 2); | ||
4231 | entry = p; | ||
4232 | |||
4233 | /* Make sure the packet actually has a value_follows and EOF entry */ | ||
4234 | if ((entry + 1) > end) | ||
4235 | goto short_pkt; | ||
4236 | |||
4237 | for (; *p++; nr++) { | ||
4238 | u32 len, attrlen, xlen; | ||
4239 | if (end - p < 3) | ||
4240 | goto short_pkt; | ||
4241 | dprintk("cookie = %Lu, ", *((unsigned long long *)p)); | ||
4242 | p += 2; /* cookie */ | ||
4243 | len = ntohl(*p++); /* filename length */ | ||
4244 | if (len > NFS4_MAXNAMLEN) { | ||
4245 | dprintk("NFS: giant filename in readdir (len 0x%x)\n", | ||
4246 | len); | ||
4247 | goto err_unmap; | ||
4248 | } | ||
4249 | xlen = XDR_QUADLEN(len); | ||
4250 | if (end - p < xlen + 1) | ||
4251 | goto short_pkt; | ||
4252 | dprintk("filename = %*s\n", len, (char *)p); | ||
4253 | p += xlen; | ||
4254 | len = ntohl(*p++); /* bitmap length */ | ||
4255 | if (end - p < len + 1) | ||
4256 | goto short_pkt; | ||
4257 | p += len; | ||
4258 | attrlen = XDR_QUADLEN(ntohl(*p++)); | ||
4259 | if (end - p < attrlen + 2) | ||
4260 | goto short_pkt; | ||
4261 | p += attrlen; /* attributes */ | ||
4262 | entry = p; | ||
4263 | } | ||
4264 | /* | ||
4265 | * Apparently some server sends responses that are a valid size, but | ||
4266 | * contain no entries, and have value_follows==0 and EOF==0. For | ||
4267 | * those, just set the EOF marker. | ||
4268 | */ | ||
4269 | if (!nr && entry[1] == 0) { | ||
4270 | dprintk("NFS: readdir reply truncated!\n"); | ||
4271 | entry[1] = 1; | ||
4272 | } | ||
4273 | out: | ||
4274 | kunmap_atomic(kaddr, KM_USER0); | ||
4275 | return 0; | 4338 | return 0; |
4276 | short_pkt: | ||
4277 | /* | ||
4278 | * When we get a short packet there are 2 possibilities. We can | ||
4279 | * return an error, or fix up the response to look like a valid | ||
4280 | * response and return what we have so far. If there are no | ||
4281 | * entries and the packet was short, then return -EIO. If there | ||
4282 | * are valid entries in the response, return them and pretend that | ||
4283 | * the call was successful, but incomplete. The caller can retry the | ||
4284 | * readdir starting at the last cookie. | ||
4285 | */ | ||
4286 | dprintk("%s: short packet at entry %d\n", __func__, nr); | ||
4287 | entry[0] = entry[1] = 0; | ||
4288 | if (nr) | ||
4289 | goto out; | ||
4290 | err_unmap: | ||
4291 | kunmap_atomic(kaddr, KM_USER0); | ||
4292 | return -errno_NFSERR_IO; | ||
4293 | } | 4339 | } |
4294 | 4340 | ||
4295 | static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) | 4341 | static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) |
@@ -4299,7 +4345,6 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) | |||
4299 | size_t hdrlen; | 4345 | size_t hdrlen; |
4300 | u32 len, recvd; | 4346 | u32 len, recvd; |
4301 | __be32 *p; | 4347 | __be32 *p; |
4302 | char *kaddr; | ||
4303 | int status; | 4348 | int status; |
4304 | 4349 | ||
4305 | status = decode_op_hdr(xdr, OP_READLINK); | 4350 | status = decode_op_hdr(xdr, OP_READLINK); |
@@ -4330,9 +4375,7 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) | |||
4330 | * and and null-terminate the text (the VFS expects | 4375 | * and and null-terminate the text (the VFS expects |
4331 | * null-termination). | 4376 | * null-termination). |
4332 | */ | 4377 | */ |
4333 | kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0); | 4378 | xdr_terminate_string(rcvbuf, len); |
4334 | kaddr[len+rcvbuf->page_base] = '\0'; | ||
4335 | kunmap_atomic(kaddr, KM_USER0); | ||
4336 | return 0; | 4379 | return 0; |
4337 | out_overflow: | 4380 | out_overflow: |
4338 | print_overflow_msg(__func__, xdr); | 4381 | print_overflow_msg(__func__, xdr); |
@@ -4668,7 +4711,6 @@ static int decode_sequence(struct xdr_stream *xdr, | |||
4668 | struct rpc_rqst *rqstp) | 4711 | struct rpc_rqst *rqstp) |
4669 | { | 4712 | { |
4670 | #if defined(CONFIG_NFS_V4_1) | 4713 | #if defined(CONFIG_NFS_V4_1) |
4671 | struct nfs4_slot *slot; | ||
4672 | struct nfs4_sessionid id; | 4714 | struct nfs4_sessionid id; |
4673 | u32 dummy; | 4715 | u32 dummy; |
4674 | int status; | 4716 | int status; |
@@ -4700,15 +4742,14 @@ static int decode_sequence(struct xdr_stream *xdr, | |||
4700 | goto out_overflow; | 4742 | goto out_overflow; |
4701 | 4743 | ||
4702 | /* seqid */ | 4744 | /* seqid */ |
4703 | slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid]; | ||
4704 | dummy = be32_to_cpup(p++); | 4745 | dummy = be32_to_cpup(p++); |
4705 | if (dummy != slot->seq_nr) { | 4746 | if (dummy != res->sr_slot->seq_nr) { |
4706 | dprintk("%s Invalid sequence number\n", __func__); | 4747 | dprintk("%s Invalid sequence number\n", __func__); |
4707 | goto out_err; | 4748 | goto out_err; |
4708 | } | 4749 | } |
4709 | /* slot id */ | 4750 | /* slot id */ |
4710 | dummy = be32_to_cpup(p++); | 4751 | dummy = be32_to_cpup(p++); |
4711 | if (dummy != res->sr_slotid) { | 4752 | if (dummy != res->sr_slot - res->sr_session->fc_slot_table.slots) { |
4712 | dprintk("%s Invalid slot id\n", __func__); | 4753 | dprintk("%s Invalid slot id\n", __func__); |
4713 | goto out_err; | 4754 | goto out_err; |
4714 | } | 4755 | } |
@@ -4873,7 +4914,7 @@ out: | |||
4873 | /* | 4914 | /* |
4874 | * Decode RENAME response | 4915 | * Decode RENAME response |
4875 | */ | 4916 | */ |
4876 | static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_rename_res *res) | 4917 | static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs_renameres *res) |
4877 | { | 4918 | { |
4878 | struct xdr_stream xdr; | 4919 | struct xdr_stream xdr; |
4879 | struct compound_hdr hdr; | 4920 | struct compound_hdr hdr; |
@@ -5760,23 +5801,35 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p, | |||
5760 | } | 5801 | } |
5761 | #endif /* CONFIG_NFS_V4_1 */ | 5802 | #endif /* CONFIG_NFS_V4_1 */ |
5762 | 5803 | ||
5763 | __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 5804 | __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, |
5805 | struct nfs_server *server, int plus) | ||
5764 | { | 5806 | { |
5765 | uint32_t bitmap[2] = {0}; | 5807 | uint32_t bitmap[2] = {0}; |
5766 | uint32_t len; | 5808 | uint32_t len; |
5767 | 5809 | __be32 *p = xdr_inline_decode(xdr, 4); | |
5768 | if (!*p++) { | 5810 | if (unlikely(!p)) |
5769 | if (!*p) | 5811 | goto out_overflow; |
5812 | if (!ntohl(*p++)) { | ||
5813 | p = xdr_inline_decode(xdr, 4); | ||
5814 | if (unlikely(!p)) | ||
5815 | goto out_overflow; | ||
5816 | if (!ntohl(*p++)) | ||
5770 | return ERR_PTR(-EAGAIN); | 5817 | return ERR_PTR(-EAGAIN); |
5771 | entry->eof = 1; | 5818 | entry->eof = 1; |
5772 | return ERR_PTR(-EBADCOOKIE); | 5819 | return ERR_PTR(-EBADCOOKIE); |
5773 | } | 5820 | } |
5774 | 5821 | ||
5822 | p = xdr_inline_decode(xdr, 12); | ||
5823 | if (unlikely(!p)) | ||
5824 | goto out_overflow; | ||
5775 | entry->prev_cookie = entry->cookie; | 5825 | entry->prev_cookie = entry->cookie; |
5776 | p = xdr_decode_hyper(p, &entry->cookie); | 5826 | p = xdr_decode_hyper(p, &entry->cookie); |
5777 | entry->len = ntohl(*p++); | 5827 | entry->len = ntohl(*p++); |
5828 | |||
5829 | p = xdr_inline_decode(xdr, entry->len); | ||
5830 | if (unlikely(!p)) | ||
5831 | goto out_overflow; | ||
5778 | entry->name = (const char *) p; | 5832 | entry->name = (const char *) p; |
5779 | p += XDR_QUADLEN(entry->len); | ||
5780 | 5833 | ||
5781 | /* | 5834 | /* |
5782 | * In case the server doesn't return an inode number, | 5835 | * In case the server doesn't return an inode number, |
@@ -5784,32 +5837,33 @@ __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | |||
5784 | * since glibc seems to choke on it...) | 5837 | * since glibc seems to choke on it...) |
5785 | */ | 5838 | */ |
5786 | entry->ino = 1; | 5839 | entry->ino = 1; |
5840 | entry->fattr->valid = 0; | ||
5787 | 5841 | ||
5788 | len = ntohl(*p++); /* bitmap length */ | 5842 | if (decode_attr_bitmap(xdr, bitmap) < 0) |
5789 | if (len-- > 0) { | 5843 | goto out_overflow; |
5790 | bitmap[0] = ntohl(*p++); | 5844 | |
5791 | if (len-- > 0) { | 5845 | if (decode_attr_length(xdr, &len, &p) < 0) |
5792 | bitmap[1] = ntohl(*p++); | 5846 | goto out_overflow; |
5793 | p += len; | 5847 | |
5794 | } | 5848 | if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, server, 1) < 0) |
5795 | } | 5849 | goto out_overflow; |
5796 | len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */ | 5850 | if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID) |
5797 | if (len > 0) { | 5851 | entry->ino = entry->fattr->fileid; |
5798 | if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) { | 5852 | |
5799 | bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; | 5853 | if (verify_attr_len(xdr, p, len) < 0) |
5800 | /* Ignore the return value of rdattr_error for now */ | 5854 | goto out_overflow; |
5801 | p++; | 5855 | |
5802 | len--; | 5856 | p = xdr_inline_peek(xdr, 8); |
5803 | } | 5857 | if (p != NULL) |
5804 | if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) | 5858 | entry->eof = !p[0] && p[1]; |
5805 | xdr_decode_hyper(p, &entry->ino); | 5859 | else |
5806 | else if (bitmap[0] == FATTR4_WORD0_FILEID) | 5860 | entry->eof = 0; |
5807 | xdr_decode_hyper(p, &entry->ino); | ||
5808 | p += len; | ||
5809 | } | ||
5810 | 5861 | ||
5811 | entry->eof = !p[0] && p[1]; | ||
5812 | return p; | 5862 | return p; |
5863 | |||
5864 | out_overflow: | ||
5865 | print_overflow_msg(__func__, xdr); | ||
5866 | return ERR_PTR(-EIO); | ||
5813 | } | 5867 | } |
5814 | 5868 | ||
5815 | /* | 5869 | /* |
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index df101d9f546a..460df3652889 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c | |||
@@ -3,9 +3,10 @@ | |||
3 | * | 3 | * |
4 | * Allow an NFS filesystem to be mounted as root. The way this works is: | 4 | * Allow an NFS filesystem to be mounted as root. The way this works is: |
5 | * (1) Use the IP autoconfig mechanism to set local IP addresses and routes. | 5 | * (1) Use the IP autoconfig mechanism to set local IP addresses and routes. |
6 | * (2) Handle RPC negotiation with the system which replied to RARP or | 6 | * (2) Construct the device string and the options string using DHCP |
7 | * was reported as a boot server by BOOTP or manually. | 7 | * option 17 and/or kernel command line options. |
8 | * (3) The actual mounting is done later, when init() is running. | 8 | * (3) When mount_root() sets up the root file system, pass these strings |
9 | * to the NFS client's regular mount interface via sys_mount(). | ||
9 | * | 10 | * |
10 | * | 11 | * |
11 | * Changes: | 12 | * Changes: |
@@ -65,470 +66,243 @@ | |||
65 | * Hua Qin : Support for mounting root file system via | 66 | * Hua Qin : Support for mounting root file system via |
66 | * NFS over TCP. | 67 | * NFS over TCP. |
67 | * Fabian Frederick: Option parser rebuilt (using parser lib) | 68 | * Fabian Frederick: Option parser rebuilt (using parser lib) |
68 | */ | 69 | * Chuck Lever : Use super.c's text-based mount option parsing |
70 | * Chuck Lever : Add "nfsrootdebug". | ||
71 | */ | ||
69 | 72 | ||
70 | #include <linux/types.h> | 73 | #include <linux/types.h> |
71 | #include <linux/string.h> | 74 | #include <linux/string.h> |
72 | #include <linux/kernel.h> | ||
73 | #include <linux/time.h> | ||
74 | #include <linux/fs.h> | ||
75 | #include <linux/init.h> | 75 | #include <linux/init.h> |
76 | #include <linux/sunrpc/clnt.h> | ||
77 | #include <linux/sunrpc/xprtsock.h> | ||
78 | #include <linux/nfs.h> | 76 | #include <linux/nfs.h> |
79 | #include <linux/nfs_fs.h> | 77 | #include <linux/nfs_fs.h> |
80 | #include <linux/nfs_mount.h> | ||
81 | #include <linux/in.h> | ||
82 | #include <linux/major.h> | ||
83 | #include <linux/utsname.h> | 78 | #include <linux/utsname.h> |
84 | #include <linux/inet.h> | ||
85 | #include <linux/root_dev.h> | 79 | #include <linux/root_dev.h> |
86 | #include <net/ipconfig.h> | 80 | #include <net/ipconfig.h> |
87 | #include <linux/parser.h> | ||
88 | 81 | ||
89 | #include "internal.h" | 82 | #include "internal.h" |
90 | 83 | ||
91 | /* Define this to allow debugging output */ | ||
92 | #undef NFSROOT_DEBUG | ||
93 | #define NFSDBG_FACILITY NFSDBG_ROOT | 84 | #define NFSDBG_FACILITY NFSDBG_ROOT |
94 | 85 | ||
95 | /* Default port to use if server is not running a portmapper */ | ||
96 | #define NFS_MNT_PORT 627 | ||
97 | |||
98 | /* Default path we try to mount. "%s" gets replaced by our IP address */ | 86 | /* Default path we try to mount. "%s" gets replaced by our IP address */ |
99 | #define NFS_ROOT "/tftpboot/%s" | 87 | #define NFS_ROOT "/tftpboot/%s" |
100 | 88 | ||
101 | /* Parameters passed from the kernel command line */ | 89 | /* Parameters passed from the kernel command line */ |
102 | static char nfs_root_name[256] __initdata = ""; | 90 | static char nfs_root_parms[256] __initdata = ""; |
91 | |||
92 | /* Text-based mount options passed to super.c */ | ||
93 | static char nfs_root_options[256] __initdata = ""; | ||
103 | 94 | ||
104 | /* Address of NFS server */ | 95 | /* Address of NFS server */ |
105 | static __be32 servaddr __initdata = 0; | 96 | static __be32 servaddr __initdata = htonl(INADDR_NONE); |
106 | 97 | ||
107 | /* Name of directory to mount */ | 98 | /* Name of directory to mount */ |
108 | static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = { 0, }; | 99 | static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = ""; |
109 | 100 | ||
110 | /* NFS-related data */ | 101 | /* server:export path string passed to super.c */ |
111 | static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ | 102 | static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = ""; |
112 | static int nfs_port __initdata = 0; /* Port to connect to for NFS */ | ||
113 | static int mount_port __initdata = 0; /* Mount daemon port number */ | ||
114 | |||
115 | |||
116 | /*************************************************************************** | ||
117 | |||
118 | Parsing of options | ||
119 | |||
120 | ***************************************************************************/ | ||
121 | |||
122 | enum { | ||
123 | /* Options that take integer arguments */ | ||
124 | Opt_port, Opt_rsize, Opt_wsize, Opt_timeo, Opt_retrans, Opt_acregmin, | ||
125 | Opt_acregmax, Opt_acdirmin, Opt_acdirmax, | ||
126 | /* Options that take no arguments */ | ||
127 | Opt_soft, Opt_hard, Opt_intr, | ||
128 | Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, | ||
129 | Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp, | ||
130 | Opt_acl, Opt_noacl, | ||
131 | /* Error token */ | ||
132 | Opt_err | ||
133 | }; | ||
134 | |||
135 | static const match_table_t tokens __initconst = { | ||
136 | {Opt_port, "port=%u"}, | ||
137 | {Opt_rsize, "rsize=%u"}, | ||
138 | {Opt_wsize, "wsize=%u"}, | ||
139 | {Opt_timeo, "timeo=%u"}, | ||
140 | {Opt_retrans, "retrans=%u"}, | ||
141 | {Opt_acregmin, "acregmin=%u"}, | ||
142 | {Opt_acregmax, "acregmax=%u"}, | ||
143 | {Opt_acdirmin, "acdirmin=%u"}, | ||
144 | {Opt_acdirmax, "acdirmax=%u"}, | ||
145 | {Opt_soft, "soft"}, | ||
146 | {Opt_hard, "hard"}, | ||
147 | {Opt_intr, "intr"}, | ||
148 | {Opt_nointr, "nointr"}, | ||
149 | {Opt_posix, "posix"}, | ||
150 | {Opt_noposix, "noposix"}, | ||
151 | {Opt_cto, "cto"}, | ||
152 | {Opt_nocto, "nocto"}, | ||
153 | {Opt_ac, "ac"}, | ||
154 | {Opt_noac, "noac"}, | ||
155 | {Opt_lock, "lock"}, | ||
156 | {Opt_nolock, "nolock"}, | ||
157 | {Opt_v2, "nfsvers=2"}, | ||
158 | {Opt_v2, "v2"}, | ||
159 | {Opt_v3, "nfsvers=3"}, | ||
160 | {Opt_v3, "v3"}, | ||
161 | {Opt_udp, "proto=udp"}, | ||
162 | {Opt_udp, "udp"}, | ||
163 | {Opt_tcp, "proto=tcp"}, | ||
164 | {Opt_tcp, "tcp"}, | ||
165 | {Opt_acl, "acl"}, | ||
166 | {Opt_noacl, "noacl"}, | ||
167 | {Opt_err, NULL} | ||
168 | |||
169 | }; | ||
170 | 103 | ||
171 | /* | 104 | /* |
172 | * Parse option string. | 105 | * When the "nfsrootdebug" kernel command line option is specified, |
106 | * enable debugging messages for NFSROOT. | ||
173 | */ | 107 | */ |
174 | 108 | static int __init nfs_root_debug(char *__unused) | |
175 | static int __init root_nfs_parse(char *name, char *buf) | ||
176 | { | 109 | { |
177 | 110 | nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT; | |
178 | char *p; | ||
179 | substring_t args[MAX_OPT_ARGS]; | ||
180 | int option; | ||
181 | |||
182 | if (!name) | ||
183 | return 1; | ||
184 | |||
185 | /* Set the NFS remote path */ | ||
186 | p = strsep(&name, ","); | ||
187 | if (p[0] != '\0' && strcmp(p, "default") != 0) | ||
188 | strlcpy(buf, p, NFS_MAXPATHLEN); | ||
189 | |||
190 | while ((p = strsep (&name, ",")) != NULL) { | ||
191 | int token; | ||
192 | if (!*p) | ||
193 | continue; | ||
194 | token = match_token(p, tokens, args); | ||
195 | |||
196 | /* %u tokens only. Beware if you add new tokens! */ | ||
197 | if (token < Opt_soft && match_int(&args[0], &option)) | ||
198 | return 0; | ||
199 | switch (token) { | ||
200 | case Opt_port: | ||
201 | nfs_port = option; | ||
202 | break; | ||
203 | case Opt_rsize: | ||
204 | nfs_data.rsize = option; | ||
205 | break; | ||
206 | case Opt_wsize: | ||
207 | nfs_data.wsize = option; | ||
208 | break; | ||
209 | case Opt_timeo: | ||
210 | nfs_data.timeo = option; | ||
211 | break; | ||
212 | case Opt_retrans: | ||
213 | nfs_data.retrans = option; | ||
214 | break; | ||
215 | case Opt_acregmin: | ||
216 | nfs_data.acregmin = option; | ||
217 | break; | ||
218 | case Opt_acregmax: | ||
219 | nfs_data.acregmax = option; | ||
220 | break; | ||
221 | case Opt_acdirmin: | ||
222 | nfs_data.acdirmin = option; | ||
223 | break; | ||
224 | case Opt_acdirmax: | ||
225 | nfs_data.acdirmax = option; | ||
226 | break; | ||
227 | case Opt_soft: | ||
228 | nfs_data.flags |= NFS_MOUNT_SOFT; | ||
229 | break; | ||
230 | case Opt_hard: | ||
231 | nfs_data.flags &= ~NFS_MOUNT_SOFT; | ||
232 | break; | ||
233 | case Opt_intr: | ||
234 | case Opt_nointr: | ||
235 | break; | ||
236 | case Opt_posix: | ||
237 | nfs_data.flags |= NFS_MOUNT_POSIX; | ||
238 | break; | ||
239 | case Opt_noposix: | ||
240 | nfs_data.flags &= ~NFS_MOUNT_POSIX; | ||
241 | break; | ||
242 | case Opt_cto: | ||
243 | nfs_data.flags &= ~NFS_MOUNT_NOCTO; | ||
244 | break; | ||
245 | case Opt_nocto: | ||
246 | nfs_data.flags |= NFS_MOUNT_NOCTO; | ||
247 | break; | ||
248 | case Opt_ac: | ||
249 | nfs_data.flags &= ~NFS_MOUNT_NOAC; | ||
250 | break; | ||
251 | case Opt_noac: | ||
252 | nfs_data.flags |= NFS_MOUNT_NOAC; | ||
253 | break; | ||
254 | case Opt_lock: | ||
255 | nfs_data.flags &= ~NFS_MOUNT_NONLM; | ||
256 | break; | ||
257 | case Opt_nolock: | ||
258 | nfs_data.flags |= NFS_MOUNT_NONLM; | ||
259 | break; | ||
260 | case Opt_v2: | ||
261 | nfs_data.flags &= ~NFS_MOUNT_VER3; | ||
262 | break; | ||
263 | case Opt_v3: | ||
264 | nfs_data.flags |= NFS_MOUNT_VER3; | ||
265 | break; | ||
266 | case Opt_udp: | ||
267 | nfs_data.flags &= ~NFS_MOUNT_TCP; | ||
268 | break; | ||
269 | case Opt_tcp: | ||
270 | nfs_data.flags |= NFS_MOUNT_TCP; | ||
271 | break; | ||
272 | case Opt_acl: | ||
273 | nfs_data.flags &= ~NFS_MOUNT_NOACL; | ||
274 | break; | ||
275 | case Opt_noacl: | ||
276 | nfs_data.flags |= NFS_MOUNT_NOACL; | ||
277 | break; | ||
278 | default: | ||
279 | printk(KERN_WARNING "Root-NFS: unknown " | ||
280 | "option: %s\n", p); | ||
281 | return 0; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | return 1; | 111 | return 1; |
286 | } | 112 | } |
287 | 113 | ||
114 | __setup("nfsrootdebug", nfs_root_debug); | ||
115 | |||
288 | /* | 116 | /* |
289 | * Prepare the NFS data structure and parse all options. | 117 | * Parse NFS server and directory information passed on the kernel |
118 | * command line. | ||
119 | * | ||
120 | * nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] | ||
121 | * | ||
122 | * If there is a "%s" token in the <root-dir> string, it is replaced | ||
123 | * by the ASCII-representation of the client's IP address. | ||
290 | */ | 124 | */ |
291 | static int __init root_nfs_name(char *name) | 125 | static int __init nfs_root_setup(char *line) |
292 | { | 126 | { |
293 | static char buf[NFS_MAXPATHLEN] __initdata; | 127 | ROOT_DEV = Root_NFS; |
294 | char *cp; | 128 | |
295 | 129 | if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { | |
296 | /* Set some default values */ | 130 | strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms)); |
297 | memset(&nfs_data, 0, sizeof(nfs_data)); | 131 | } else { |
298 | nfs_port = -1; | 132 | size_t n = strlen(line) + sizeof(NFS_ROOT) - 1; |
299 | nfs_data.version = NFS_MOUNT_VERSION; | 133 | if (n >= sizeof(nfs_root_parms)) |
300 | nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ | 134 | line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0'; |
301 | nfs_data.rsize = NFS_DEF_FILE_IO_SIZE; | 135 | sprintf(nfs_root_parms, NFS_ROOT, line); |
302 | nfs_data.wsize = NFS_DEF_FILE_IO_SIZE; | ||
303 | nfs_data.acregmin = NFS_DEF_ACREGMIN; | ||
304 | nfs_data.acregmax = NFS_DEF_ACREGMAX; | ||
305 | nfs_data.acdirmin = NFS_DEF_ACDIRMIN; | ||
306 | nfs_data.acdirmax = NFS_DEF_ACDIRMAX; | ||
307 | strcpy(buf, NFS_ROOT); | ||
308 | |||
309 | /* Process options received from the remote server */ | ||
310 | root_nfs_parse(root_server_path, buf); | ||
311 | |||
312 | /* Override them by options set on kernel command-line */ | ||
313 | root_nfs_parse(name, buf); | ||
314 | |||
315 | cp = utsname()->nodename; | ||
316 | if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) { | ||
317 | printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n"); | ||
318 | return -1; | ||
319 | } | 136 | } |
320 | sprintf(nfs_export_path, buf, cp); | 137 | |
138 | /* | ||
139 | * Extract the IP address of the NFS server containing our | ||
140 | * root file system, if one was specified. | ||
141 | * | ||
142 | * Note: root_nfs_parse_addr() removes the server-ip from | ||
143 | * nfs_root_parms, if it exists. | ||
144 | */ | ||
145 | root_server_addr = root_nfs_parse_addr(nfs_root_parms); | ||
321 | 146 | ||
322 | return 1; | 147 | return 1; |
323 | } | 148 | } |
324 | 149 | ||
150 | __setup("nfsroot=", nfs_root_setup); | ||
325 | 151 | ||
326 | /* | 152 | static int __init root_nfs_copy(char *dest, const char *src, |
327 | * Get NFS server address. | 153 | const size_t destlen) |
328 | */ | ||
329 | static int __init root_nfs_addr(void) | ||
330 | { | 154 | { |
331 | if ((servaddr = root_server_addr) == htonl(INADDR_NONE)) { | 155 | if (strlcpy(dest, src, destlen) > destlen) |
332 | printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n"); | ||
333 | return -1; | 156 | return -1; |
334 | } | 157 | return 0; |
158 | } | ||
335 | 159 | ||
336 | snprintf(nfs_data.hostname, sizeof(nfs_data.hostname), | 160 | static int __init root_nfs_cat(char *dest, const char *src, |
337 | "%pI4", &servaddr); | 161 | const size_t destlen) |
162 | { | ||
163 | if (strlcat(dest, src, destlen) > destlen) | ||
164 | return -1; | ||
338 | return 0; | 165 | return 0; |
339 | } | 166 | } |
340 | 167 | ||
341 | /* | 168 | /* |
342 | * Tell the user what's going on. | 169 | * Parse out root export path and mount options from |
170 | * passed-in string @incoming. | ||
171 | * | ||
172 | * Copy the export path into @exppath. | ||
343 | */ | 173 | */ |
344 | #ifdef NFSROOT_DEBUG | 174 | static int __init root_nfs_parse_options(char *incoming, char *exppath, |
345 | static void __init root_nfs_print(void) | 175 | const size_t exppathlen) |
346 | { | 176 | { |
347 | printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n", | 177 | char *p; |
348 | nfs_export_path, nfs_data.hostname); | ||
349 | printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", | ||
350 | nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans); | ||
351 | printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n", | ||
352 | nfs_data.acregmin, nfs_data.acregmax, | ||
353 | nfs_data.acdirmin, nfs_data.acdirmax); | ||
354 | printk(KERN_NOTICE "Root-NFS: nfsd port = %d, mountd port = %d, flags = %08x\n", | ||
355 | nfs_port, mount_port, nfs_data.flags); | ||
356 | } | ||
357 | #endif | ||
358 | |||
359 | 178 | ||
360 | static int __init root_nfs_init(void) | 179 | /* |
361 | { | 180 | * Set the NFS remote path |
362 | #ifdef NFSROOT_DEBUG | 181 | */ |
363 | nfs_debug |= NFSDBG_ROOT; | 182 | p = strsep(&incoming, ","); |
364 | #endif | 183 | if (*p != '\0' && strcmp(p, "default") != 0) |
184 | if (root_nfs_copy(exppath, p, exppathlen)) | ||
185 | return -1; | ||
365 | 186 | ||
366 | /* | 187 | /* |
367 | * Decode the root directory path name and NFS options from | 188 | * @incoming now points to the rest of the string; if it |
368 | * the kernel command line. This has to go here in order to | 189 | * contains something, append it to our root options buffer |
369 | * be able to use the client IP address for the remote root | ||
370 | * directory (necessary for pure RARP booting). | ||
371 | */ | 190 | */ |
372 | if (root_nfs_name(nfs_root_name) < 0 || | 191 | if (incoming != NULL && *incoming != '\0') |
373 | root_nfs_addr() < 0) | 192 | if (root_nfs_cat(nfs_root_options, incoming, |
374 | return -1; | 193 | sizeof(nfs_root_options))) |
194 | return -1; | ||
375 | 195 | ||
376 | #ifdef NFSROOT_DEBUG | 196 | /* |
377 | root_nfs_print(); | 197 | * Possibly prepare for more options to be appended |
378 | #endif | 198 | */ |
199 | if (nfs_root_options[0] != '\0' && | ||
200 | nfs_root_options[strlen(nfs_root_options)] != ',') | ||
201 | if (root_nfs_cat(nfs_root_options, ",", | ||
202 | sizeof(nfs_root_options))) | ||
203 | return -1; | ||
379 | 204 | ||
380 | return 0; | 205 | return 0; |
381 | } | 206 | } |
382 | 207 | ||
383 | |||
384 | /* | 208 | /* |
385 | * Parse NFS server and directory information passed on the kernel | 209 | * Decode the export directory path name and NFS options from |
386 | * command line. | 210 | * the kernel command line. This has to be done late in order to |
211 | * use a dynamically acquired client IP address for the remote | ||
212 | * root directory path. | ||
213 | * | ||
214 | * Returns zero if successful; otherwise -1 is returned. | ||
387 | */ | 215 | */ |
388 | static int __init nfs_root_setup(char *line) | 216 | static int __init root_nfs_data(char *cmdline) |
389 | { | 217 | { |
390 | ROOT_DEV = Root_NFS; | 218 | char addr_option[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1]; |
391 | if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { | 219 | int len, retval = -1; |
392 | strlcpy(nfs_root_name, line, sizeof(nfs_root_name)); | 220 | char *tmp = NULL; |
393 | } else { | 221 | const size_t tmplen = sizeof(nfs_export_path); |
394 | int n = strlen(line) + sizeof(NFS_ROOT) - 1; | 222 | |
395 | if (n >= sizeof(nfs_root_name)) | 223 | tmp = kzalloc(tmplen, GFP_KERNEL); |
396 | line[sizeof(nfs_root_name) - sizeof(NFS_ROOT) - 2] = '\0'; | 224 | if (tmp == NULL) |
397 | sprintf(nfs_root_name, NFS_ROOT, line); | 225 | goto out_nomem; |
226 | strcpy(tmp, NFS_ROOT); | ||
227 | |||
228 | if (root_server_path[0] != '\0') { | ||
229 | dprintk("Root-NFS: DHCPv4 option 17: %s\n", | ||
230 | root_server_path); | ||
231 | if (root_nfs_parse_options(root_server_path, tmp, tmplen)) | ||
232 | goto out_optionstoolong; | ||
398 | } | 233 | } |
399 | root_server_addr = root_nfs_parse_addr(nfs_root_name); | ||
400 | return 1; | ||
401 | } | ||
402 | 234 | ||
403 | __setup("nfsroot=", nfs_root_setup); | 235 | if (cmdline[0] != '\0') { |
404 | 236 | dprintk("Root-NFS: nfsroot=%s\n", cmdline); | |
405 | /*************************************************************************** | 237 | if (root_nfs_parse_options(cmdline, tmp, tmplen)) |
406 | 238 | goto out_optionstoolong; | |
407 | Routines to actually mount the root directory | 239 | } |
408 | 240 | ||
409 | ***************************************************************************/ | 241 | /* |
242 | * Append mandatory options for nfsroot so they override | ||
243 | * what has come before | ||
244 | */ | ||
245 | snprintf(addr_option, sizeof(addr_option), "nolock,addr=%pI4", | ||
246 | &servaddr); | ||
247 | if (root_nfs_cat(nfs_root_options, addr_option, | ||
248 | sizeof(nfs_root_options))) | ||
249 | goto out_optionstoolong; | ||
410 | 250 | ||
411 | /* | 251 | /* |
412 | * Construct sockaddr_in from address and port number. | 252 | * Set up nfs_root_device. For NFS mounts, this looks like |
413 | */ | 253 | * |
414 | static inline void | 254 | * server:/path |
415 | set_sockaddr(struct sockaddr_in *sin, __be32 addr, __be16 port) | 255 | * |
416 | { | 256 | * At this point, utsname()->nodename contains our local |
417 | sin->sin_family = AF_INET; | 257 | * IP address or hostname, set by ipconfig. If "%s" exists |
418 | sin->sin_addr.s_addr = addr; | 258 | * in tmp, substitute the nodename, then shovel the whole |
419 | sin->sin_port = port; | 259 | * mess into nfs_root_device. |
420 | } | 260 | */ |
261 | len = snprintf(nfs_export_path, sizeof(nfs_export_path), | ||
262 | tmp, utsname()->nodename); | ||
263 | if (len > (int)sizeof(nfs_export_path)) | ||
264 | goto out_devnametoolong; | ||
265 | len = snprintf(nfs_root_device, sizeof(nfs_root_device), | ||
266 | "%pI4:%s", &servaddr, nfs_export_path); | ||
267 | if (len > (int)sizeof(nfs_root_device)) | ||
268 | goto out_devnametoolong; | ||
421 | 269 | ||
422 | /* | 270 | retval = 0; |
423 | * Query server portmapper for the port of a daemon program. | ||
424 | */ | ||
425 | static int __init root_nfs_getport(int program, int version, int proto) | ||
426 | { | ||
427 | struct sockaddr_in sin; | ||
428 | 271 | ||
429 | printk(KERN_NOTICE "Looking up port of RPC %d/%d on %pI4\n", | 272 | out: |
430 | program, version, &servaddr); | 273 | kfree(tmp); |
431 | set_sockaddr(&sin, servaddr, 0); | 274 | return retval; |
432 | return rpcb_getport_sync(&sin, program, version, proto); | 275 | out_nomem: |
276 | printk(KERN_ERR "Root-NFS: could not allocate memory\n"); | ||
277 | goto out; | ||
278 | out_optionstoolong: | ||
279 | printk(KERN_ERR "Root-NFS: mount options string too long\n"); | ||
280 | goto out; | ||
281 | out_devnametoolong: | ||
282 | printk(KERN_ERR "Root-NFS: root device name too long.\n"); | ||
283 | goto out; | ||
433 | } | 284 | } |
434 | 285 | ||
435 | 286 | /** | |
436 | /* | 287 | * nfs_root_data - Return prepared 'data' for NFSROOT mount |
437 | * Use portmapper to find mountd and nfsd port numbers if not overriden | 288 | * @root_device: OUT: address of string containing NFSROOT device |
438 | * by the user. Use defaults if portmapper is not available. | 289 | * @root_data: OUT: address of string containing NFSROOT mount options |
439 | * XXX: Is there any nfs server with no portmapper? | 290 | * |
291 | * Returns zero and sets @root_device and @root_data if successful, | ||
292 | * otherwise -1 is returned. | ||
440 | */ | 293 | */ |
441 | static int __init root_nfs_ports(void) | 294 | int __init nfs_root_data(char **root_device, char **root_data) |
442 | { | 295 | { |
443 | int port; | 296 | servaddr = root_server_addr; |
444 | int nfsd_ver, mountd_ver; | 297 | if (servaddr == htonl(INADDR_NONE)) { |
445 | int nfsd_port, mountd_port; | 298 | printk(KERN_ERR "Root-NFS: no NFS server address\n"); |
446 | int proto; | 299 | return -1; |
447 | |||
448 | if (nfs_data.flags & NFS_MOUNT_VER3) { | ||
449 | nfsd_ver = NFS3_VERSION; | ||
450 | mountd_ver = NFS_MNT3_VERSION; | ||
451 | nfsd_port = NFS_PORT; | ||
452 | mountd_port = NFS_MNT_PORT; | ||
453 | } else { | ||
454 | nfsd_ver = NFS2_VERSION; | ||
455 | mountd_ver = NFS_MNT_VERSION; | ||
456 | nfsd_port = NFS_PORT; | ||
457 | mountd_port = NFS_MNT_PORT; | ||
458 | } | ||
459 | |||
460 | proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; | ||
461 | |||
462 | if (nfs_port < 0) { | ||
463 | if ((port = root_nfs_getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) { | ||
464 | printk(KERN_ERR "Root-NFS: Unable to get nfsd port " | ||
465 | "number from server, using default\n"); | ||
466 | port = nfsd_port; | ||
467 | } | ||
468 | nfs_port = port; | ||
469 | dprintk("Root-NFS: Portmapper on server returned %d " | ||
470 | "as nfsd port\n", port); | ||
471 | } | 300 | } |
472 | 301 | ||
473 | if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) { | 302 | if (root_nfs_data(nfs_root_parms) < 0) |
474 | printk(KERN_ERR "Root-NFS: Unable to get mountd port " | 303 | return -1; |
475 | "number from server, using default\n"); | ||
476 | port = mountd_port; | ||
477 | } | ||
478 | mount_port = port; | ||
479 | dprintk("Root-NFS: mountd port is %d\n", port); | ||
480 | 304 | ||
305 | *root_device = nfs_root_device; | ||
306 | *root_data = nfs_root_options; | ||
481 | return 0; | 307 | return 0; |
482 | } | 308 | } |
483 | |||
484 | |||
485 | /* | ||
486 | * Get a file handle from the server for the directory which is to be | ||
487 | * mounted. | ||
488 | */ | ||
489 | static int __init root_nfs_get_handle(void) | ||
490 | { | ||
491 | struct sockaddr_in sin; | ||
492 | unsigned int auth_flav_len = 0; | ||
493 | struct nfs_mount_request request = { | ||
494 | .sap = (struct sockaddr *)&sin, | ||
495 | .salen = sizeof(sin), | ||
496 | .dirpath = nfs_export_path, | ||
497 | .version = (nfs_data.flags & NFS_MOUNT_VER3) ? | ||
498 | NFS_MNT3_VERSION : NFS_MNT_VERSION, | ||
499 | .protocol = (nfs_data.flags & NFS_MOUNT_TCP) ? | ||
500 | XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP, | ||
501 | .auth_flav_len = &auth_flav_len, | ||
502 | }; | ||
503 | int status = -ENOMEM; | ||
504 | |||
505 | request.fh = nfs_alloc_fhandle(); | ||
506 | if (!request.fh) | ||
507 | goto out; | ||
508 | set_sockaddr(&sin, servaddr, htons(mount_port)); | ||
509 | status = nfs_mount(&request); | ||
510 | if (status < 0) | ||
511 | printk(KERN_ERR "Root-NFS: Server returned error %d " | ||
512 | "while mounting %s\n", status, nfs_export_path); | ||
513 | else { | ||
514 | nfs_data.root.size = request.fh->size; | ||
515 | memcpy(&nfs_data.root.data, request.fh->data, request.fh->size); | ||
516 | } | ||
517 | nfs_free_fhandle(request.fh); | ||
518 | out: | ||
519 | return status; | ||
520 | } | ||
521 | |||
522 | /* | ||
523 | * Get the NFS port numbers and file handle, and return the prepared 'data' | ||
524 | * argument for mount() if everything went OK. Return NULL otherwise. | ||
525 | */ | ||
526 | void * __init nfs_root_data(void) | ||
527 | { | ||
528 | if (root_nfs_init() < 0 | ||
529 | || root_nfs_ports() < 0 | ||
530 | || root_nfs_get_handle() < 0) | ||
531 | return NULL; | ||
532 | set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, htons(nfs_port)); | ||
533 | return (void*)&nfs_data; | ||
534 | } | ||
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 611bec22f552..58e7f84fc1fd 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c | |||
@@ -258,7 +258,7 @@ static void nfs_free_createdata(const struct nfs_createdata *data) | |||
258 | 258 | ||
259 | static int | 259 | static int |
260 | nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, | 260 | nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, |
261 | int flags, struct nameidata *nd) | 261 | int flags, struct nfs_open_context *ctx) |
262 | { | 262 | { |
263 | struct nfs_createdata *data; | 263 | struct nfs_createdata *data; |
264 | struct rpc_message msg = { | 264 | struct rpc_message msg = { |
@@ -365,17 +365,32 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) | |||
365 | return 1; | 365 | return 1; |
366 | } | 366 | } |
367 | 367 | ||
368 | static void | ||
369 | nfs_proc_rename_setup(struct rpc_message *msg, struct inode *dir) | ||
370 | { | ||
371 | msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME]; | ||
372 | } | ||
373 | |||
374 | static int | ||
375 | nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir, | ||
376 | struct inode *new_dir) | ||
377 | { | ||
378 | if (nfs_async_handle_expired_key(task)) | ||
379 | return 0; | ||
380 | nfs_mark_for_revalidate(old_dir); | ||
381 | nfs_mark_for_revalidate(new_dir); | ||
382 | return 1; | ||
383 | } | ||
384 | |||
368 | static int | 385 | static int |
369 | nfs_proc_rename(struct inode *old_dir, struct qstr *old_name, | 386 | nfs_proc_rename(struct inode *old_dir, struct qstr *old_name, |
370 | struct inode *new_dir, struct qstr *new_name) | 387 | struct inode *new_dir, struct qstr *new_name) |
371 | { | 388 | { |
372 | struct nfs_renameargs arg = { | 389 | struct nfs_renameargs arg = { |
373 | .fromfh = NFS_FH(old_dir), | 390 | .old_dir = NFS_FH(old_dir), |
374 | .fromname = old_name->name, | 391 | .old_name = old_name, |
375 | .fromlen = old_name->len, | 392 | .new_dir = NFS_FH(new_dir), |
376 | .tofh = NFS_FH(new_dir), | 393 | .new_name = new_name, |
377 | .toname = new_name->name, | ||
378 | .tolen = new_name->len | ||
379 | }; | 394 | }; |
380 | struct rpc_message msg = { | 395 | struct rpc_message msg = { |
381 | .rpc_proc = &nfs_procedures[NFSPROC_RENAME], | 396 | .rpc_proc = &nfs_procedures[NFSPROC_RENAME], |
@@ -519,14 +534,14 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name) | |||
519 | */ | 534 | */ |
520 | static int | 535 | static int |
521 | nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 536 | nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
522 | u64 cookie, struct page *page, unsigned int count, int plus) | 537 | u64 cookie, struct page **pages, unsigned int count, int plus) |
523 | { | 538 | { |
524 | struct inode *dir = dentry->d_inode; | 539 | struct inode *dir = dentry->d_inode; |
525 | struct nfs_readdirargs arg = { | 540 | struct nfs_readdirargs arg = { |
526 | .fh = NFS_FH(dir), | 541 | .fh = NFS_FH(dir), |
527 | .cookie = cookie, | 542 | .cookie = cookie, |
528 | .count = count, | 543 | .count = count, |
529 | .pages = &page, | 544 | .pages = pages, |
530 | }; | 545 | }; |
531 | struct rpc_message msg = { | 546 | struct rpc_message msg = { |
532 | .rpc_proc = &nfs_procedures[NFSPROC_READDIR], | 547 | .rpc_proc = &nfs_procedures[NFSPROC_READDIR], |
@@ -705,6 +720,8 @@ const struct nfs_rpc_ops nfs_v2_clientops = { | |||
705 | .unlink_setup = nfs_proc_unlink_setup, | 720 | .unlink_setup = nfs_proc_unlink_setup, |
706 | .unlink_done = nfs_proc_unlink_done, | 721 | .unlink_done = nfs_proc_unlink_done, |
707 | .rename = nfs_proc_rename, | 722 | .rename = nfs_proc_rename, |
723 | .rename_setup = nfs_proc_rename_setup, | ||
724 | .rename_done = nfs_proc_rename_done, | ||
708 | .link = nfs_proc_link, | 725 | .link = nfs_proc_link, |
709 | .symlink = nfs_proc_symlink, | 726 | .symlink = nfs_proc_symlink, |
710 | .mkdir = nfs_proc_mkdir, | 727 | .mkdir = nfs_proc_mkdir, |
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 87adc2744246..79859c81a943 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c | |||
@@ -46,7 +46,6 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) | |||
46 | memset(p, 0, sizeof(*p)); | 46 | memset(p, 0, sizeof(*p)); |
47 | INIT_LIST_HEAD(&p->pages); | 47 | INIT_LIST_HEAD(&p->pages); |
48 | p->npages = pagecount; | 48 | p->npages = pagecount; |
49 | p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
50 | if (pagecount <= ARRAY_SIZE(p->page_array)) | 49 | if (pagecount <= ARRAY_SIZE(p->page_array)) |
51 | p->pagevec = p->page_array; | 50 | p->pagevec = p->page_array; |
52 | else { | 51 | else { |
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index f4cbf0c306c6..3600ec700d58 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -100,6 +100,7 @@ enum { | |||
100 | Opt_addr, Opt_mountaddr, Opt_clientaddr, | 100 | Opt_addr, Opt_mountaddr, Opt_clientaddr, |
101 | Opt_lookupcache, | 101 | Opt_lookupcache, |
102 | Opt_fscache_uniq, | 102 | Opt_fscache_uniq, |
103 | Opt_local_lock, | ||
103 | 104 | ||
104 | /* Special mount options */ | 105 | /* Special mount options */ |
105 | Opt_userspace, Opt_deprecated, Opt_sloppy, | 106 | Opt_userspace, Opt_deprecated, Opt_sloppy, |
@@ -171,6 +172,7 @@ static const match_table_t nfs_mount_option_tokens = { | |||
171 | 172 | ||
172 | { Opt_lookupcache, "lookupcache=%s" }, | 173 | { Opt_lookupcache, "lookupcache=%s" }, |
173 | { Opt_fscache_uniq, "fsc=%s" }, | 174 | { Opt_fscache_uniq, "fsc=%s" }, |
175 | { Opt_local_lock, "local_lock=%s" }, | ||
174 | 176 | ||
175 | { Opt_err, NULL } | 177 | { Opt_err, NULL } |
176 | }; | 178 | }; |
@@ -236,6 +238,22 @@ static match_table_t nfs_lookupcache_tokens = { | |||
236 | { Opt_lookupcache_err, NULL } | 238 | { Opt_lookupcache_err, NULL } |
237 | }; | 239 | }; |
238 | 240 | ||
241 | enum { | ||
242 | Opt_local_lock_all, Opt_local_lock_flock, Opt_local_lock_posix, | ||
243 | Opt_local_lock_none, | ||
244 | |||
245 | Opt_local_lock_err | ||
246 | }; | ||
247 | |||
248 | static match_table_t nfs_local_lock_tokens = { | ||
249 | { Opt_local_lock_all, "all" }, | ||
250 | { Opt_local_lock_flock, "flock" }, | ||
251 | { Opt_local_lock_posix, "posix" }, | ||
252 | { Opt_local_lock_none, "none" }, | ||
253 | |||
254 | { Opt_local_lock_err, NULL } | ||
255 | }; | ||
256 | |||
239 | 257 | ||
240 | static void nfs_umount_begin(struct super_block *); | 258 | static void nfs_umount_begin(struct super_block *); |
241 | static int nfs_statfs(struct dentry *, struct kstatfs *); | 259 | static int nfs_statfs(struct dentry *, struct kstatfs *); |
@@ -622,6 +640,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, | |||
622 | const struct proc_nfs_info *nfs_infop; | 640 | const struct proc_nfs_info *nfs_infop; |
623 | struct nfs_client *clp = nfss->nfs_client; | 641 | struct nfs_client *clp = nfss->nfs_client; |
624 | u32 version = clp->rpc_ops->version; | 642 | u32 version = clp->rpc_ops->version; |
643 | int local_flock, local_fcntl; | ||
625 | 644 | ||
626 | seq_printf(m, ",vers=%u", version); | 645 | seq_printf(m, ",vers=%u", version); |
627 | seq_printf(m, ",rsize=%u", nfss->rsize); | 646 | seq_printf(m, ",rsize=%u", nfss->rsize); |
@@ -670,6 +689,18 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, | |||
670 | else | 689 | else |
671 | seq_printf(m, ",lookupcache=pos"); | 690 | seq_printf(m, ",lookupcache=pos"); |
672 | } | 691 | } |
692 | |||
693 | local_flock = nfss->flags & NFS_MOUNT_LOCAL_FLOCK; | ||
694 | local_fcntl = nfss->flags & NFS_MOUNT_LOCAL_FCNTL; | ||
695 | |||
696 | if (!local_flock && !local_fcntl) | ||
697 | seq_printf(m, ",local_lock=none"); | ||
698 | else if (local_flock && local_fcntl) | ||
699 | seq_printf(m, ",local_lock=all"); | ||
700 | else if (local_flock) | ||
701 | seq_printf(m, ",local_lock=flock"); | ||
702 | else | ||
703 | seq_printf(m, ",local_lock=posix"); | ||
673 | } | 704 | } |
674 | 705 | ||
675 | /* | 706 | /* |
@@ -1017,9 +1048,13 @@ static int nfs_parse_mount_options(char *raw, | |||
1017 | break; | 1048 | break; |
1018 | case Opt_lock: | 1049 | case Opt_lock: |
1019 | mnt->flags &= ~NFS_MOUNT_NONLM; | 1050 | mnt->flags &= ~NFS_MOUNT_NONLM; |
1051 | mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | | ||
1052 | NFS_MOUNT_LOCAL_FCNTL); | ||
1020 | break; | 1053 | break; |
1021 | case Opt_nolock: | 1054 | case Opt_nolock: |
1022 | mnt->flags |= NFS_MOUNT_NONLM; | 1055 | mnt->flags |= NFS_MOUNT_NONLM; |
1056 | mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK | | ||
1057 | NFS_MOUNT_LOCAL_FCNTL); | ||
1023 | break; | 1058 | break; |
1024 | case Opt_v2: | 1059 | case Opt_v2: |
1025 | mnt->flags &= ~NFS_MOUNT_VER3; | 1060 | mnt->flags &= ~NFS_MOUNT_VER3; |
@@ -1420,6 +1455,34 @@ static int nfs_parse_mount_options(char *raw, | |||
1420 | mnt->fscache_uniq = string; | 1455 | mnt->fscache_uniq = string; |
1421 | mnt->options |= NFS_OPTION_FSCACHE; | 1456 | mnt->options |= NFS_OPTION_FSCACHE; |
1422 | break; | 1457 | break; |
1458 | case Opt_local_lock: | ||
1459 | string = match_strdup(args); | ||
1460 | if (string == NULL) | ||
1461 | goto out_nomem; | ||
1462 | token = match_token(string, nfs_local_lock_tokens, | ||
1463 | args); | ||
1464 | kfree(string); | ||
1465 | switch (token) { | ||
1466 | case Opt_local_lock_all: | ||
1467 | mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK | | ||
1468 | NFS_MOUNT_LOCAL_FCNTL); | ||
1469 | break; | ||
1470 | case Opt_local_lock_flock: | ||
1471 | mnt->flags |= NFS_MOUNT_LOCAL_FLOCK; | ||
1472 | break; | ||
1473 | case Opt_local_lock_posix: | ||
1474 | mnt->flags |= NFS_MOUNT_LOCAL_FCNTL; | ||
1475 | break; | ||
1476 | case Opt_local_lock_none: | ||
1477 | mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | | ||
1478 | NFS_MOUNT_LOCAL_FCNTL); | ||
1479 | break; | ||
1480 | default: | ||
1481 | dfprintk(MOUNT, "NFS: invalid " | ||
1482 | "local_lock argument\n"); | ||
1483 | return 0; | ||
1484 | }; | ||
1485 | break; | ||
1423 | 1486 | ||
1424 | /* | 1487 | /* |
1425 | * Special options | 1488 | * Special options |
@@ -1825,6 +1888,12 @@ static int nfs_validate_mount_data(void *options, | |||
1825 | if (!args->nfs_server.hostname) | 1888 | if (!args->nfs_server.hostname) |
1826 | goto out_nomem; | 1889 | goto out_nomem; |
1827 | 1890 | ||
1891 | if (!(data->flags & NFS_MOUNT_NONLM)) | ||
1892 | args->flags &= ~(NFS_MOUNT_LOCAL_FLOCK| | ||
1893 | NFS_MOUNT_LOCAL_FCNTL); | ||
1894 | else | ||
1895 | args->flags |= (NFS_MOUNT_LOCAL_FLOCK| | ||
1896 | NFS_MOUNT_LOCAL_FCNTL); | ||
1828 | /* | 1897 | /* |
1829 | * The legacy version 6 binary mount data from userspace has a | 1898 | * The legacy version 6 binary mount data from userspace has a |
1830 | * field used only to transport selinux information into the | 1899 | * field used only to transport selinux information into the |
@@ -2441,7 +2510,8 @@ static void nfs4_fill_super(struct super_block *sb) | |||
2441 | 2510 | ||
2442 | static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) | 2511 | static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) |
2443 | { | 2512 | { |
2444 | args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3); | 2513 | args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3| |
2514 | NFS_MOUNT_LOCAL_FLOCK|NFS_MOUNT_LOCAL_FCNTL); | ||
2445 | } | 2515 | } |
2446 | 2516 | ||
2447 | static int nfs4_validate_text_mount_data(void *options, | 2517 | static int nfs4_validate_text_mount_data(void *options, |
diff --git a/fs/nfs/sysctl.c b/fs/nfs/sysctl.c index ad4d2e787b20..978aaeb8a093 100644 --- a/fs/nfs/sysctl.c +++ b/fs/nfs/sysctl.c | |||
@@ -32,6 +32,7 @@ static ctl_table nfs_cb_sysctls[] = { | |||
32 | .extra1 = (int *)&nfs_set_port_min, | 32 | .extra1 = (int *)&nfs_set_port_min, |
33 | .extra2 = (int *)&nfs_set_port_max, | 33 | .extra2 = (int *)&nfs_set_port_max, |
34 | }, | 34 | }, |
35 | #ifndef CONFIG_NFS_USE_NEW_IDMAPPER | ||
35 | { | 36 | { |
36 | .procname = "idmap_cache_timeout", | 37 | .procname = "idmap_cache_timeout", |
37 | .data = &nfs_idmap_cache_timeout, | 38 | .data = &nfs_idmap_cache_timeout, |
@@ -39,6 +40,7 @@ static ctl_table nfs_cb_sysctls[] = { | |||
39 | .mode = 0644, | 40 | .mode = 0644, |
40 | .proc_handler = proc_dointvec_jiffies, | 41 | .proc_handler = proc_dointvec_jiffies, |
41 | }, | 42 | }, |
43 | #endif /* CONFIG_NFS_USE_NEW_IDMAPPER */ | ||
42 | #endif | 44 | #endif |
43 | { | 45 | { |
44 | .procname = "nfs_mountpoint_timeout", | 46 | .procname = "nfs_mountpoint_timeout", |
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 2f84adaad427..9a16bad5d2ea 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c | |||
@@ -13,9 +13,12 @@ | |||
13 | #include <linux/nfs_fs.h> | 13 | #include <linux/nfs_fs.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/wait.h> | 15 | #include <linux/wait.h> |
16 | #include <linux/namei.h> | ||
16 | 17 | ||
17 | #include "internal.h" | 18 | #include "internal.h" |
18 | #include "nfs4_fs.h" | 19 | #include "nfs4_fs.h" |
20 | #include "iostat.h" | ||
21 | #include "delegation.h" | ||
19 | 22 | ||
20 | struct nfs_unlinkdata { | 23 | struct nfs_unlinkdata { |
21 | struct hlist_node list; | 24 | struct hlist_node list; |
@@ -244,7 +247,7 @@ void nfs_unblock_sillyrename(struct dentry *dentry) | |||
244 | * @dir: parent directory of dentry | 247 | * @dir: parent directory of dentry |
245 | * @dentry: dentry to unlink | 248 | * @dentry: dentry to unlink |
246 | */ | 249 | */ |
247 | int | 250 | static int |
248 | nfs_async_unlink(struct inode *dir, struct dentry *dentry) | 251 | nfs_async_unlink(struct inode *dir, struct dentry *dentry) |
249 | { | 252 | { |
250 | struct nfs_unlinkdata *data; | 253 | struct nfs_unlinkdata *data; |
@@ -259,7 +262,6 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) | |||
259 | status = PTR_ERR(data->cred); | 262 | status = PTR_ERR(data->cred); |
260 | goto out_free; | 263 | goto out_free; |
261 | } | 264 | } |
262 | data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
263 | data->res.dir_attr = &data->dir_attr; | 265 | data->res.dir_attr = &data->dir_attr; |
264 | 266 | ||
265 | status = -EBUSY; | 267 | status = -EBUSY; |
@@ -303,3 +305,256 @@ nfs_complete_unlink(struct dentry *dentry, struct inode *inode) | |||
303 | if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) | 305 | if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) |
304 | nfs_free_unlinkdata(data); | 306 | nfs_free_unlinkdata(data); |
305 | } | 307 | } |
308 | |||
309 | /* Cancel a queued async unlink. Called when a sillyrename run fails. */ | ||
310 | static void | ||
311 | nfs_cancel_async_unlink(struct dentry *dentry) | ||
312 | { | ||
313 | spin_lock(&dentry->d_lock); | ||
314 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { | ||
315 | struct nfs_unlinkdata *data = dentry->d_fsdata; | ||
316 | |||
317 | dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; | ||
318 | spin_unlock(&dentry->d_lock); | ||
319 | nfs_free_unlinkdata(data); | ||
320 | return; | ||
321 | } | ||
322 | spin_unlock(&dentry->d_lock); | ||
323 | } | ||
324 | |||
325 | struct nfs_renamedata { | ||
326 | struct nfs_renameargs args; | ||
327 | struct nfs_renameres res; | ||
328 | struct rpc_cred *cred; | ||
329 | struct inode *old_dir; | ||
330 | struct dentry *old_dentry; | ||
331 | struct nfs_fattr old_fattr; | ||
332 | struct inode *new_dir; | ||
333 | struct dentry *new_dentry; | ||
334 | struct nfs_fattr new_fattr; | ||
335 | }; | ||
336 | |||
337 | /** | ||
338 | * nfs_async_rename_done - Sillyrename post-processing | ||
339 | * @task: rpc_task of the sillyrename | ||
340 | * @calldata: nfs_renamedata for the sillyrename | ||
341 | * | ||
342 | * Do the directory attribute updates and the d_move | ||
343 | */ | ||
344 | static void nfs_async_rename_done(struct rpc_task *task, void *calldata) | ||
345 | { | ||
346 | struct nfs_renamedata *data = calldata; | ||
347 | struct inode *old_dir = data->old_dir; | ||
348 | struct inode *new_dir = data->new_dir; | ||
349 | |||
350 | if (!NFS_PROTO(old_dir)->rename_done(task, old_dir, new_dir)) { | ||
351 | nfs_restart_rpc(task, NFS_SERVER(old_dir)->nfs_client); | ||
352 | return; | ||
353 | } | ||
354 | |||
355 | if (task->tk_status != 0) { | ||
356 | nfs_cancel_async_unlink(data->old_dentry); | ||
357 | return; | ||
358 | } | ||
359 | |||
360 | nfs_set_verifier(data->old_dentry, nfs_save_change_attribute(old_dir)); | ||
361 | d_move(data->old_dentry, data->new_dentry); | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * nfs_async_rename_release - Release the sillyrename data. | ||
366 | * @calldata: the struct nfs_renamedata to be released | ||
367 | */ | ||
368 | static void nfs_async_rename_release(void *calldata) | ||
369 | { | ||
370 | struct nfs_renamedata *data = calldata; | ||
371 | struct super_block *sb = data->old_dir->i_sb; | ||
372 | |||
373 | if (data->old_dentry->d_inode) | ||
374 | nfs_mark_for_revalidate(data->old_dentry->d_inode); | ||
375 | |||
376 | dput(data->old_dentry); | ||
377 | dput(data->new_dentry); | ||
378 | iput(data->old_dir); | ||
379 | iput(data->new_dir); | ||
380 | nfs_sb_deactive(sb); | ||
381 | put_rpccred(data->cred); | ||
382 | kfree(data); | ||
383 | } | ||
384 | |||
385 | #if defined(CONFIG_NFS_V4_1) | ||
386 | static void nfs_rename_prepare(struct rpc_task *task, void *calldata) | ||
387 | { | ||
388 | struct nfs_renamedata *data = calldata; | ||
389 | struct nfs_server *server = NFS_SERVER(data->old_dir); | ||
390 | |||
391 | if (nfs4_setup_sequence(server, &data->args.seq_args, | ||
392 | &data->res.seq_res, 1, task)) | ||
393 | return; | ||
394 | rpc_call_start(task); | ||
395 | } | ||
396 | #endif /* CONFIG_NFS_V4_1 */ | ||
397 | |||
398 | static const struct rpc_call_ops nfs_rename_ops = { | ||
399 | .rpc_call_done = nfs_async_rename_done, | ||
400 | .rpc_release = nfs_async_rename_release, | ||
401 | #if defined(CONFIG_NFS_V4_1) | ||
402 | .rpc_call_prepare = nfs_rename_prepare, | ||
403 | #endif /* CONFIG_NFS_V4_1 */ | ||
404 | }; | ||
405 | |||
406 | /** | ||
407 | * nfs_async_rename - perform an asynchronous rename operation | ||
408 | * @old_dir: directory that currently holds the dentry to be renamed | ||
409 | * @new_dir: target directory for the rename | ||
410 | * @old_dentry: original dentry to be renamed | ||
411 | * @new_dentry: dentry to which the old_dentry should be renamed | ||
412 | * | ||
413 | * It's expected that valid references to the dentries and inodes are held | ||
414 | */ | ||
415 | static struct rpc_task * | ||
416 | nfs_async_rename(struct inode *old_dir, struct inode *new_dir, | ||
417 | struct dentry *old_dentry, struct dentry *new_dentry) | ||
418 | { | ||
419 | struct nfs_renamedata *data; | ||
420 | struct rpc_message msg = { }; | ||
421 | struct rpc_task_setup task_setup_data = { | ||
422 | .rpc_message = &msg, | ||
423 | .callback_ops = &nfs_rename_ops, | ||
424 | .workqueue = nfsiod_workqueue, | ||
425 | .rpc_client = NFS_CLIENT(old_dir), | ||
426 | .flags = RPC_TASK_ASYNC, | ||
427 | }; | ||
428 | |||
429 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
430 | if (data == NULL) | ||
431 | return ERR_PTR(-ENOMEM); | ||
432 | task_setup_data.callback_data = data, | ||
433 | |||
434 | data->cred = rpc_lookup_cred(); | ||
435 | if (IS_ERR(data->cred)) { | ||
436 | struct rpc_task *task = ERR_CAST(data->cred); | ||
437 | kfree(data); | ||
438 | return task; | ||
439 | } | ||
440 | |||
441 | msg.rpc_argp = &data->args; | ||
442 | msg.rpc_resp = &data->res; | ||
443 | msg.rpc_cred = data->cred; | ||
444 | |||
445 | /* set up nfs_renamedata */ | ||
446 | data->old_dir = old_dir; | ||
447 | atomic_inc(&old_dir->i_count); | ||
448 | data->new_dir = new_dir; | ||
449 | atomic_inc(&new_dir->i_count); | ||
450 | data->old_dentry = dget(old_dentry); | ||
451 | data->new_dentry = dget(new_dentry); | ||
452 | nfs_fattr_init(&data->old_fattr); | ||
453 | nfs_fattr_init(&data->new_fattr); | ||
454 | |||
455 | /* set up nfs_renameargs */ | ||
456 | data->args.old_dir = NFS_FH(old_dir); | ||
457 | data->args.old_name = &old_dentry->d_name; | ||
458 | data->args.new_dir = NFS_FH(new_dir); | ||
459 | data->args.new_name = &new_dentry->d_name; | ||
460 | |||
461 | /* set up nfs_renameres */ | ||
462 | data->res.old_fattr = &data->old_fattr; | ||
463 | data->res.new_fattr = &data->new_fattr; | ||
464 | |||
465 | nfs_sb_active(old_dir->i_sb); | ||
466 | |||
467 | NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dir); | ||
468 | |||
469 | return rpc_run_task(&task_setup_data); | ||
470 | } | ||
471 | |||
472 | /** | ||
473 | * nfs_sillyrename - Perform a silly-rename of a dentry | ||
474 | * @dir: inode of directory that contains dentry | ||
475 | * @dentry: dentry to be sillyrenamed | ||
476 | * | ||
477 | * NFSv2/3 is stateless and the server doesn't know when the client is | ||
478 | * holding a file open. To prevent application problems when a file is | ||
479 | * unlinked while it's still open, the client performs a "silly-rename". | ||
480 | * That is, it renames the file to a hidden file in the same directory, | ||
481 | * and only performs the unlink once the last reference to it is put. | ||
482 | * | ||
483 | * The final cleanup is done during dentry_iput. | ||
484 | */ | ||
485 | int | ||
486 | nfs_sillyrename(struct inode *dir, struct dentry *dentry) | ||
487 | { | ||
488 | static unsigned int sillycounter; | ||
489 | const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; | ||
490 | const int countersize = sizeof(sillycounter)*2; | ||
491 | const int slen = sizeof(".nfs")+fileidsize+countersize-1; | ||
492 | char silly[slen+1]; | ||
493 | struct dentry *sdentry; | ||
494 | struct rpc_task *task; | ||
495 | int error = -EIO; | ||
496 | |||
497 | dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", | ||
498 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
499 | atomic_read(&dentry->d_count)); | ||
500 | nfs_inc_stats(dir, NFSIOS_SILLYRENAME); | ||
501 | |||
502 | /* | ||
503 | * We don't allow a dentry to be silly-renamed twice. | ||
504 | */ | ||
505 | error = -EBUSY; | ||
506 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
507 | goto out; | ||
508 | |||
509 | sprintf(silly, ".nfs%*.*Lx", | ||
510 | fileidsize, fileidsize, | ||
511 | (unsigned long long)NFS_FILEID(dentry->d_inode)); | ||
512 | |||
513 | /* Return delegation in anticipation of the rename */ | ||
514 | nfs_inode_return_delegation(dentry->d_inode); | ||
515 | |||
516 | sdentry = NULL; | ||
517 | do { | ||
518 | char *suffix = silly + slen - countersize; | ||
519 | |||
520 | dput(sdentry); | ||
521 | sillycounter++; | ||
522 | sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); | ||
523 | |||
524 | dfprintk(VFS, "NFS: trying to rename %s to %s\n", | ||
525 | dentry->d_name.name, silly); | ||
526 | |||
527 | sdentry = lookup_one_len(silly, dentry->d_parent, slen); | ||
528 | /* | ||
529 | * N.B. Better to return EBUSY here ... it could be | ||
530 | * dangerous to delete the file while it's in use. | ||
531 | */ | ||
532 | if (IS_ERR(sdentry)) | ||
533 | goto out; | ||
534 | } while (sdentry->d_inode != NULL); /* need negative lookup */ | ||
535 | |||
536 | /* queue unlink first. Can't do this from rpc_release as it | ||
537 | * has to allocate memory | ||
538 | */ | ||
539 | error = nfs_async_unlink(dir, dentry); | ||
540 | if (error) | ||
541 | goto out_dput; | ||
542 | |||
543 | /* run the rename task, undo unlink if it fails */ | ||
544 | task = nfs_async_rename(dir, dir, dentry, sdentry); | ||
545 | if (IS_ERR(task)) { | ||
546 | error = -EBUSY; | ||
547 | nfs_cancel_async_unlink(dentry); | ||
548 | goto out_dput; | ||
549 | } | ||
550 | |||
551 | /* wait for the RPC task to complete, unless a SIGKILL intervenes */ | ||
552 | error = rpc_wait_for_completion_task(task); | ||
553 | if (error == 0) | ||
554 | error = task->tk_status; | ||
555 | rpc_put_task(task); | ||
556 | out_dput: | ||
557 | dput(sdentry); | ||
558 | out: | ||
559 | return error; | ||
560 | } | ||
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 874972d9427c..605e292501f4 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -55,7 +55,6 @@ struct nfs_write_data *nfs_commitdata_alloc(void) | |||
55 | if (p) { | 55 | if (p) { |
56 | memset(p, 0, sizeof(*p)); | 56 | memset(p, 0, sizeof(*p)); |
57 | INIT_LIST_HEAD(&p->pages); | 57 | INIT_LIST_HEAD(&p->pages); |
58 | p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
59 | } | 58 | } |
60 | return p; | 59 | return p; |
61 | } | 60 | } |
@@ -75,7 +74,6 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) | |||
75 | memset(p, 0, sizeof(*p)); | 74 | memset(p, 0, sizeof(*p)); |
76 | INIT_LIST_HEAD(&p->pages); | 75 | INIT_LIST_HEAD(&p->pages); |
77 | p->npages = pagecount; | 76 | p->npages = pagecount; |
78 | p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; | ||
79 | if (pagecount <= ARRAY_SIZE(p->page_array)) | 77 | if (pagecount <= ARRAY_SIZE(p->page_array)) |
80 | p->pagevec = p->page_array; | 78 | p->pagevec = p->page_array; |
81 | else { | 79 | else { |
@@ -1433,15 +1431,17 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr | |||
1433 | int flags = FLUSH_SYNC; | 1431 | int flags = FLUSH_SYNC; |
1434 | int ret = 0; | 1432 | int ret = 0; |
1435 | 1433 | ||
1436 | /* Don't commit yet if this is a non-blocking flush and there are | 1434 | if (wbc->sync_mode == WB_SYNC_NONE) { |
1437 | * lots of outstanding writes for this mapping. | 1435 | /* Don't commit yet if this is a non-blocking flush and there |
1438 | */ | 1436 | * are a lot of outstanding writes for this mapping. |
1439 | if (wbc->sync_mode == WB_SYNC_NONE && | 1437 | */ |
1440 | nfsi->ncommit <= (nfsi->npages >> 1)) | 1438 | if (nfsi->ncommit <= (nfsi->npages >> 1)) |
1441 | goto out_mark_dirty; | 1439 | goto out_mark_dirty; |
1442 | 1440 | ||
1443 | if (wbc->nonblocking || wbc->for_background) | 1441 | /* don't wait for the COMMIT response */ |
1444 | flags = 0; | 1442 | flags = 0; |
1443 | } | ||
1444 | |||
1445 | ret = nfs_commit_inode(inode, flags); | 1445 | ret = nfs_commit_inode(inode, flags); |
1446 | if (ret >= 0) { | 1446 | if (ret >= 0) { |
1447 | if (wbc->sync_mode == WB_SYNC_NONE) { | 1447 | if (wbc->sync_mode == WB_SYNC_NONE) { |