nfs4proc.c 96.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
 *  fs/nfs/nfs4proc.c
 *
 *  Client-side procedure declarations for NFSv4.
 *
 *  Copyright (c) 2002 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Kendrick Smith <kmsmith@umich.edu>
 *  Andy Adamson   <andros@umich.edu>
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the University nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <linux/mm.h>
#include <linux/utsname.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
#include <linux/smp_lock.h>
#include <linux/namei.h>
50
#include <linux/mount.h>
Linus Torvalds's avatar
Linus Torvalds committed
51

52
#include "nfs4_fs.h"
Linus Torvalds's avatar
Linus Torvalds committed
53
54
55
56
57
58
59
#include "delegation.h"

#define NFSDBG_FACILITY		NFSDBG_PROC

#define NFS4_POLL_RETRY_MIN	(1*HZ)
#define NFS4_POLL_RETRY_MAX	(15*HZ)

60
struct nfs4_opendata;
61
static int _nfs4_proc_open(struct nfs4_opendata *data);
Linus Torvalds's avatar
Linus Torvalds committed
62
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
63
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
Linus Torvalds's avatar
Linus Torvalds committed
64
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
65
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
66
static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp);
Linus Torvalds's avatar
Linus Torvalds committed
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[];

/* Prevent leaks of NFSv4 errors into userland */
int nfs4_map_errors(int err)
{
	if (err < -1000) {
		dprintk("%s could not handle NFSv4 error %d\n",
				__FUNCTION__, -err);
		return -EIO;
	}
	return err;
}

/*
 * This is our standard bitmap for GETATTR requests.
 */
const u32 nfs4_fattr_bitmap[2] = {
	FATTR4_WORD0_TYPE
	| FATTR4_WORD0_CHANGE
	| FATTR4_WORD0_SIZE
	| FATTR4_WORD0_FSID
	| FATTR4_WORD0_FILEID,
	FATTR4_WORD1_MODE
	| FATTR4_WORD1_NUMLINKS
	| FATTR4_WORD1_OWNER
	| FATTR4_WORD1_OWNER_GROUP
	| FATTR4_WORD1_RAWDEV
	| FATTR4_WORD1_SPACE_USED
	| FATTR4_WORD1_TIME_ACCESS
	| FATTR4_WORD1_TIME_METADATA
	| FATTR4_WORD1_TIME_MODIFY
};

const u32 nfs4_statfs_bitmap[2] = {
	FATTR4_WORD0_FILES_AVAIL
	| FATTR4_WORD0_FILES_FREE
	| FATTR4_WORD0_FILES_TOTAL,
	FATTR4_WORD1_SPACE_AVAIL
	| FATTR4_WORD1_SPACE_FREE
	| FATTR4_WORD1_SPACE_TOTAL
};

110
const u32 nfs4_pathconf_bitmap[2] = {
Linus Torvalds's avatar
Linus Torvalds committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
	FATTR4_WORD0_MAXLINK
	| FATTR4_WORD0_MAXNAME,
	0
};

const u32 nfs4_fsinfo_bitmap[2] = { FATTR4_WORD0_MAXFILESIZE
			| FATTR4_WORD0_MAXREAD
			| FATTR4_WORD0_MAXWRITE
			| FATTR4_WORD0_LEASE_TIME,
			0
};

static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
		struct nfs4_readdir_arg *readdir)
{
	u32 *start, *p;

	BUG_ON(readdir->count < 80);
	if (cookie > 2) {
130
		readdir->cookie = cookie;
Linus Torvalds's avatar
Linus Torvalds committed
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
		memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier));
		return;
	}

	readdir->cookie = 0;
	memset(&readdir->verifier, 0, sizeof(readdir->verifier));
	if (cookie == 2)
		return;
	
	/*
	 * NFSv4 servers do not return entries for '.' and '..'
	 * Therefore, we fake these entries here.  We let '.'
	 * have cookie 0 and '..' have cookie 1.  Note that
	 * when talking to the server, we always send cookie 0
	 * instead of 1 or 2.
	 */
	start = p = (u32 *)kmap_atomic(*readdir->pages, KM_USER0);
	
	if (cookie == 0) {
		*p++ = xdr_one;                                  /* next */
		*p++ = xdr_zero;                   /* cookie, first word */
		*p++ = xdr_one;                   /* cookie, second word */
		*p++ = xdr_one;                             /* entry len */
		memcpy(p, ".\0\0\0", 4);                        /* entry */
		p++;
		*p++ = xdr_one;                         /* bitmap length */
		*p++ = htonl(FATTR4_WORD0_FILEID);             /* bitmap */
		*p++ = htonl(8);              /* attribute buffer length */
		p = xdr_encode_hyper(p, dentry->d_inode->i_ino);
	}
	
	*p++ = xdr_one;                                  /* next */
	*p++ = xdr_zero;                   /* cookie, first word */
	*p++ = xdr_two;                   /* cookie, second word */
	*p++ = xdr_two;                             /* entry len */
	memcpy(p, "..\0\0", 4);                         /* entry */
	p++;
	*p++ = xdr_one;                         /* bitmap length */
	*p++ = htonl(FATTR4_WORD0_FILEID);             /* bitmap */
	*p++ = htonl(8);              /* attribute buffer length */
	p = xdr_encode_hyper(p, dentry->d_parent->d_inode->i_ino);

	readdir->pgbase = (char *)p - (char *)start;
	readdir->count -= readdir->pgbase;
	kunmap_atomic(start, KM_USER0);
}

178
static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
Linus Torvalds's avatar
Linus Torvalds committed
179
180
181
182
183
184
185
186
187
188
189
190
{
	struct nfs4_client *clp = server->nfs4_state;
	spin_lock(&clp->cl_lock);
	if (time_before(clp->cl_last_renewal,timestamp))
		clp->cl_last_renewal = timestamp;
	spin_unlock(&clp->cl_lock);
}

static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinfo)
{
	struct nfs_inode *nfsi = NFS_I(inode);

191
192
	spin_lock(&inode->i_lock);
	nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
Linus Torvalds's avatar
Linus Torvalds committed
193
194
	if (cinfo->before == nfsi->change_attr && cinfo->atomic)
		nfsi->change_attr = cinfo->after;
195
	spin_unlock(&inode->i_lock);
Linus Torvalds's avatar
Linus Torvalds committed
196
197
}

198
struct nfs4_opendata {
199
	atomic_t count;
200
201
	struct nfs_openargs o_arg;
	struct nfs_openres o_res;
202
203
	struct nfs_open_confirmargs c_arg;
	struct nfs_open_confirmres c_res;
204
205
206
207
208
209
	struct nfs_fattr f_attr;
	struct nfs_fattr dir_attr;
	struct dentry *dentry;
	struct dentry *dir;
	struct nfs4_state_owner *owner;
	struct iattr attrs;
210
	unsigned long timestamp;
211
212
	int rpc_status;
	int cancelled;
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
};

static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
		struct nfs4_state_owner *sp, int flags,
		const struct iattr *attrs)
{
	struct dentry *parent = dget_parent(dentry);
	struct inode *dir = parent->d_inode;
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs4_opendata *p;

	p = kzalloc(sizeof(*p), GFP_KERNEL);
	if (p == NULL)
		goto err;
	p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
	if (p->o_arg.seqid == NULL)
		goto err_free;
230
	atomic_set(&p->count, 1);
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
	p->dentry = dget(dentry);
	p->dir = parent;
	p->owner = sp;
	atomic_inc(&sp->so_count);
	p->o_arg.fh = NFS_FH(dir);
	p->o_arg.open_flags = flags,
	p->o_arg.clientid = server->nfs4_state->cl_clientid;
	p->o_arg.id = sp->so_id;
	p->o_arg.name = &dentry->d_name;
	p->o_arg.server = server;
	p->o_arg.bitmask = server->attr_bitmask;
	p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
	p->o_res.f_attr = &p->f_attr;
	p->o_res.dir_attr = &p->dir_attr;
	p->o_res.server = server;
	nfs_fattr_init(&p->f_attr);
	nfs_fattr_init(&p->dir_attr);
	if (flags & O_EXCL) {
		u32 *s = (u32 *) p->o_arg.u.verifier.data;
		s[0] = jiffies;
		s[1] = current->pid;
	} else if (flags & O_CREAT) {
		p->o_arg.u.attrs = &p->attrs;
		memcpy(&p->attrs, attrs, sizeof(p->attrs));
	}
256
257
258
	p->c_arg.fh = &p->o_res.fh;
	p->c_arg.stateid = &p->o_res.stateid;
	p->c_arg.seqid = p->o_arg.seqid;
259
260
261
262
263
264
265
266
267
268
	return p;
err_free:
	kfree(p);
err:
	dput(parent);
	return NULL;
}

static void nfs4_opendata_free(struct nfs4_opendata *p)
{
269
	if (p != NULL && atomic_dec_and_test(&p->count)) {
270
271
272
273
274
275
276
277
		nfs_free_seqid(p->o_arg.seqid);
		nfs4_put_state_owner(p->owner);
		dput(p->dir);
		dput(p->dentry);
		kfree(p);
	}
}

278
/* Helper for asynchronous RPC calls */
Trond Myklebust's avatar
Trond Myklebust committed
279
static int nfs4_call_async(struct rpc_clnt *clnt,
280
		const struct rpc_call_ops *tk_ops, void *calldata)
281
282
283
{
	struct rpc_task *task;

284
	if (!(task = rpc_new_task(clnt, RPC_TASK_ASYNC, tk_ops, calldata)))
285
286
287
288
289
		return -ENOMEM;
	rpc_execute(task);
	return 0;
}

290
291
292
293
294
295
296
297
298
299
300
static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
{
	sigset_t oldset;
	int ret;

	rpc_clnt_sigmask(task->tk_client, &oldset);
	ret = rpc_wait_for_completion_task(task);
	rpc_clnt_sigunmask(task->tk_client, &oldset);
	return ret;
}

301
302
303
304
305
306
307
308
309
310
311
312
313
314
static inline void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
{
	switch (open_flags) {
		case FMODE_WRITE:
			state->n_wronly++;
			break;
		case FMODE_READ:
			state->n_rdonly++;
			break;
		case FMODE_READ|FMODE_WRITE:
			state->n_rdwr++;
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
315
316
317
318
319
static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
{
	struct inode *inode = state->inode;

	open_flags &= (FMODE_READ|FMODE_WRITE);
320
	/* Protect against nfs4_find_state_byowner() */
321
	spin_lock(&state->owner->so_lock);
Linus Torvalds's avatar
Linus Torvalds committed
322
	spin_lock(&inode->i_lock);
323
	memcpy(&state->stateid, stateid, sizeof(state->stateid));
324
	update_open_stateflags(state, open_flags);
325
	nfs4_state_set_mode_locked(state, state->state | open_flags);
Linus Torvalds's avatar
Linus Torvalds committed
326
	spin_unlock(&inode->i_lock);
327
	spin_unlock(&state->owner->so_lock);
Linus Torvalds's avatar
Linus Torvalds committed
328
329
}

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
{
	struct inode *inode;
	struct nfs4_state *state = NULL;

	if (!(data->f_attr.valid & NFS_ATTR_FATTR))
		goto out;
	inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
	if (inode == NULL)
		goto out;
	state = nfs4_get_open_state(inode, data->owner);
	if (state == NULL)
		goto put_inode;
	update_open_stateid(state, &data->o_res.stateid, data->o_arg.open_flags);
put_inode:
	iput(inode);
out:
	return state;
}

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state)
{
	struct nfs_inode *nfsi = NFS_I(state->inode);
	struct nfs_open_context *ctx;

	spin_lock(&state->inode->i_lock);
	list_for_each_entry(ctx, &nfsi->open_files, list) {
		if (ctx->state != state)
			continue;
		get_nfs_open_context(ctx);
		spin_unlock(&state->inode->i_lock);
		return ctx;
	}
	spin_unlock(&state->inode->i_lock);
	return ERR_PTR(-ENOENT);
}

static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, nfs4_stateid *stateid)
{
	int ret;

	opendata->o_arg.open_flags = openflags;
	ret = _nfs4_proc_open(opendata);
	if (ret != 0)
		return ret; 
	memcpy(stateid->data, opendata->o_res.stateid.data,
			sizeof(stateid->data));
	return 0;
}

static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)
{
	nfs4_stateid stateid;
	struct nfs4_state *newstate;
	int mode = 0;
	int delegation = 0;
	int ret;

	/* memory barrier prior to reading state->n_* */
	smp_rmb();
	if (state->n_rdwr != 0) {
		ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &stateid);
		if (ret != 0)
			return ret;
		mode |= FMODE_READ|FMODE_WRITE;
		if (opendata->o_res.delegation_type != 0)
			delegation = opendata->o_res.delegation_type;
		smp_rmb();
	}
	if (state->n_wronly != 0) {
		ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &stateid);
		if (ret != 0)
			return ret;
		mode |= FMODE_WRITE;
		if (opendata->o_res.delegation_type != 0)
			delegation = opendata->o_res.delegation_type;
		smp_rmb();
	}
	if (state->n_rdonly != 0) {
		ret = nfs4_open_recover_helper(opendata, FMODE_READ, &stateid);
		if (ret != 0)
			return ret;
		mode |= FMODE_READ;
	}
	clear_bit(NFS_DELEGATED_STATE, &state->flags);
	if (mode == 0)
		return 0;
	if (opendata->o_res.delegation_type == 0)
		opendata->o_res.delegation_type = delegation;
	opendata->o_arg.open_flags |= mode;
	newstate = nfs4_opendata_to_nfs4_state(opendata);
	if (newstate != NULL) {
		if (opendata->o_res.delegation_type != 0) {
			struct nfs_inode *nfsi = NFS_I(newstate->inode);
			int delegation_flags = 0;
			if (nfsi->delegation)
				delegation_flags = nfsi->delegation->flags;
			if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM))
				nfs_inode_set_delegation(newstate->inode,
						opendata->owner->so_cred,
						&opendata->o_res);
			else
				nfs_inode_reclaim_delegation(newstate->inode,
						opendata->owner->so_cred,
						&opendata->o_res);
		}
		nfs4_close_state(newstate, opendata->o_arg.open_flags);
	}
	if (newstate != state)
		return -ESTALE;
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
443
444
445
446
/*
 * OPEN_RECLAIM:
 * 	reclaim state on the server after a reboot.
 */
447
static int _nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry)
Linus Torvalds's avatar
Linus Torvalds committed
448
{
449
450
451
	struct nfs_delegation *delegation = NFS_I(state->inode)->delegation;
	struct nfs4_opendata *opendata;
	int delegation_type = 0;
Linus Torvalds's avatar
Linus Torvalds committed
452
453
454
455
456
457
458
459
460
	int status;

	if (delegation != NULL) {
		if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) {
			memcpy(&state->stateid, &delegation->stateid,
					sizeof(state->stateid));
			set_bit(NFS_DELEGATED_STATE, &state->flags);
			return 0;
		}
461
		delegation_type = delegation->type;
Linus Torvalds's avatar
Linus Torvalds committed
462
	}
463
464
	opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL);
	if (opendata == NULL)
465
		return -ENOMEM;
466
467
468
469
470
471
	opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS;
	opendata->o_arg.fh = NFS_FH(state->inode);
	nfs_copy_fh(&opendata->o_res.fh, opendata->o_arg.fh);
	opendata->o_arg.u.delegation_type = delegation_type;
	status = nfs4_open_recover(opendata, state);
	nfs4_opendata_free(opendata);
Linus Torvalds's avatar
Linus Torvalds committed
472
473
474
	return status;
}

475
static int nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry)
Linus Torvalds's avatar
Linus Torvalds committed
476
477
478
479
480
{
	struct nfs_server *server = NFS_SERVER(state->inode);
	struct nfs4_exception exception = { };
	int err;
	do {
481
		err = _nfs4_do_open_reclaim(sp, state, dentry);
482
483
484
		if (err != -NFS4ERR_DELAY)
			break;
		nfs4_handle_exception(server, err, &exception);
Linus Torvalds's avatar
Linus Torvalds committed
485
486
487
488
	} while (exception.retry);
	return err;
}

489
490
491
492
493
494
495
496
497
498
499
500
501
static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
	struct nfs_open_context *ctx;
	int ret;

	ctx = nfs4_state_find_open_context(state);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);
	ret = nfs4_do_open_reclaim(sp, state, ctx->dentry);
	put_nfs_open_context(ctx);
	return ret;
}

Linus Torvalds's avatar
Linus Torvalds committed
502
503
504
static int _nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state)
{
	struct nfs4_state_owner  *sp  = state->owner;
505
	struct nfs4_opendata *opendata;
506
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
507
508

	if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
509
510
		return 0;
	opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL);
511
	if (opendata == NULL)
512
		return -ENOMEM;
513
514
515
	opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR;
	memcpy(opendata->o_arg.u.delegation.data, state->stateid.data,
			sizeof(opendata->o_arg.u.delegation.data));
516
	ret = nfs4_open_recover(opendata, state);
517
	nfs4_opendata_free(opendata);
518
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
}

int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state)
{
	struct nfs4_exception exception = { };
	struct nfs_server *server = NFS_SERVER(dentry->d_inode);
	int err;
	do {
		err = _nfs4_open_delegation_recall(dentry, state);
		switch (err) {
			case 0:
				return err;
			case -NFS4ERR_STALE_CLIENTID:
			case -NFS4ERR_STALE_STATEID:
			case -NFS4ERR_EXPIRED:
				/* Don't recall a delegation if it was lost */
				nfs4_schedule_state_recovery(server->nfs4_state);
				return err;
		}
		err = nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
	return err;
}

543
static void nfs4_open_confirm_prepare(struct rpc_task *task, void *calldata)
Linus Torvalds's avatar
Linus Torvalds committed
544
{
545
546
547
548
549
550
	struct nfs4_opendata *data = calldata;
	struct  rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
		.rpc_argp = &data->c_arg,
		.rpc_resp = &data->c_res,
		.rpc_cred = data->owner->so_cred,
Linus Torvalds's avatar
Linus Torvalds committed
551
	};
552
	data->timestamp = jiffies;
553
554
555
556
557
558
559
560
561
562
	rpc_call_setup(task, &msg, 0);
}

static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_opendata *data = calldata;

	data->rpc_status = task->tk_status;
	if (RPC_ASSASSINATED(task))
		return;
563
	if (data->rpc_status == 0) {
564
565
		memcpy(data->o_res.stateid.data, data->c_res.stateid.data,
				sizeof(data->o_res.stateid.data));
566
567
		renew_lease(data->o_res.server, data->timestamp);
	}
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
	nfs_increment_open_seqid(data->rpc_status, data->c_arg.seqid);
	nfs_confirm_seqid(&data->owner->so_seqid, data->rpc_status);
}

static void nfs4_open_confirm_release(void *calldata)
{
	struct nfs4_opendata *data = calldata;
	struct nfs4_state *state = NULL;

	/* If this request hasn't been cancelled, do nothing */
	if (data->cancelled == 0)
		goto out_free;
	/* In case of error, no cleanup! */
	if (data->rpc_status != 0)
		goto out_free;
	nfs_confirm_seqid(&data->owner->so_seqid, 0);
	state = nfs4_opendata_to_nfs4_state(data);
	if (state != NULL)
		nfs4_close_state(state, data->o_arg.open_flags);
out_free:
	nfs4_opendata_free(data);
}

static const struct rpc_call_ops nfs4_open_confirm_ops = {
	.rpc_call_prepare = nfs4_open_confirm_prepare,
	.rpc_call_done = nfs4_open_confirm_done,
	.rpc_release = nfs4_open_confirm_release,
};

/*
 * Note: On error, nfs4_proc_open_confirm will free the struct nfs4_opendata
 */
static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
{
	struct nfs_server *server = NFS_SERVER(data->dir->d_inode);
	struct rpc_task *task;
Linus Torvalds's avatar
Linus Torvalds committed
604
605
	int status;

606
607
608
609
610
611
612
613
614
615
616
617
618
	atomic_inc(&data->count);
	task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_confirm_ops, data);
	if (IS_ERR(task)) {
		nfs4_opendata_free(data);
		return PTR_ERR(task);
	}
	status = nfs4_wait_for_completion_rpc_task(task);
	if (status != 0) {
		data->cancelled = 1;
		smp_wmb();
	} else
		status = data->rpc_status;
	rpc_release_task(task);
Linus Torvalds's avatar
Linus Torvalds committed
619
620
621
	return status;
}

622
static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
Linus Torvalds's avatar
Linus Torvalds committed
623
{
624
625
	struct nfs4_opendata *data = calldata;
	struct nfs4_state_owner *sp = data->owner;
Linus Torvalds's avatar
Linus Torvalds committed
626
627
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN],
628
629
		.rpc_argp = &data->o_arg,
		.rpc_resp = &data->o_res,
Linus Torvalds's avatar
Linus Torvalds committed
630
631
		.rpc_cred = sp->so_cred,
	};
632
633
634
635
636
637
	
	if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
		return;
	/* Update sequence id. */
	data->o_arg.id = sp->so_id;
	data->o_arg.clientid = sp->so_client->cl_clientid;
638
639
	if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS)
		msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
640
	data->timestamp = jiffies;
641
642
	rpc_call_setup(task, &msg, 0);
}
Linus Torvalds's avatar
Linus Torvalds committed
643

644
645
646
static void nfs4_open_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_opendata *data = calldata;
Linus Torvalds's avatar
Linus Torvalds committed
647

648
649
650
651
652
	data->rpc_status = task->tk_status;
	if (RPC_ASSASSINATED(task))
		return;
	if (task->tk_status == 0) {
		switch (data->o_res.f_attr->mode & S_IFMT) {
653
654
655
			case S_IFREG:
				break;
			case S_IFLNK:
656
				data->rpc_status = -ELOOP;
657
658
				break;
			case S_IFDIR:
659
				data->rpc_status = -EISDIR;
660
661
				break;
			default:
662
				data->rpc_status = -ENOTDIR;
663
		}
664
		renew_lease(data->o_res.server, data->timestamp);
665
	}
666
667
	nfs_increment_open_seqid(data->rpc_status, data->o_arg.seqid);
}
668

669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
static void nfs4_open_release(void *calldata)
{
	struct nfs4_opendata *data = calldata;
	struct nfs4_state *state = NULL;

	/* If this request hasn't been cancelled, do nothing */
	if (data->cancelled == 0)
		goto out_free;
	/* In case of error, no cleanup! */
	if (data->rpc_status != 0)
		goto out_free;
	/* In case we need an open_confirm, no cleanup! */
	if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM)
		goto out_free;
	nfs_confirm_seqid(&data->owner->so_seqid, 0);
	state = nfs4_opendata_to_nfs4_state(data);
	if (state != NULL)
		nfs4_close_state(state, data->o_arg.open_flags);
out_free:
	nfs4_opendata_free(data);
}

static const struct rpc_call_ops nfs4_open_ops = {
	.rpc_call_prepare = nfs4_open_prepare,
	.rpc_call_done = nfs4_open_done,
	.rpc_release = nfs4_open_release,
};

/*
 * Note: On error, nfs4_proc_open will free the struct nfs4_opendata
 */
static int _nfs4_proc_open(struct nfs4_opendata *data)
{
	struct inode *dir = data->dir->d_inode;
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs_openargs *o_arg = &data->o_arg;
	struct nfs_openres *o_res = &data->o_res;
	struct rpc_task *task;
	int status;

	atomic_inc(&data->count);
	task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_ops, data);
	if (IS_ERR(task)) {
		nfs4_opendata_free(data);
		return PTR_ERR(task);
	}
	status = nfs4_wait_for_completion_rpc_task(task);
	if (status != 0) {
		data->cancelled = 1;
		smp_wmb();
	} else
		status = data->rpc_status;
	rpc_release_task(task);
Linus Torvalds's avatar
Linus Torvalds committed
722
	if (status != 0)
723
724
		return status;

725
726
727
728
729
	if (o_arg->open_flags & O_CREAT) {
		update_changeattr(dir, &o_res->cinfo);
		nfs_post_op_update_inode(dir, o_res->dir_attr);
	} else
		nfs_refresh_inode(dir, o_res->dir_attr);
Linus Torvalds's avatar
Linus Torvalds committed
730
	if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
731
		status = _nfs4_proc_open_confirm(data);
Linus Torvalds's avatar
Linus Torvalds committed
732
		if (status != 0)
733
			return status;
Linus Torvalds's avatar
Linus Torvalds committed
734
	}
735
	nfs_confirm_seqid(&data->owner->so_seqid, 0);
Linus Torvalds's avatar
Linus Torvalds committed
736
	if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
737
738
		return server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
}

static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags)
{
	struct nfs_access_entry cache;
	int mask = 0;
	int status;

	if (openflags & FMODE_READ)
		mask |= MAY_READ;
	if (openflags & FMODE_WRITE)
		mask |= MAY_WRITE;
	status = nfs_access_get_cached(inode, cred, &cache);
	if (status == 0)
		goto out;

	/* Be clever: ask server to check for all possible rights */
	cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
	cache.cred = cred;
	cache.jiffies = jiffies;
	status = _nfs4_proc_access(inode, &cache);
	if (status != 0)
		return status;
	nfs_access_add_cache(inode, &cache);
out:
	if ((cache.mask & mask) == mask)
		return 0;
	return -EACCES;
}

769
770
771
772
773
774
775
776
777
int nfs4_recover_expired_lease(struct nfs_server *server)
{
	struct nfs4_client *clp = server->nfs4_state;

	if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
		nfs4_schedule_state_recovery(clp);
	return nfs4_wait_clnt_recover(server->client, clp);
}

Linus Torvalds's avatar
Linus Torvalds committed
778
779
780
781
782
783
784
785
786
/*
 * OPEN_EXPIRED:
 * 	reclaim state on the server after a network partition.
 * 	Assumes caller holds the appropriate lock
 */
static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry)
{
	struct inode *inode = state->inode;
	struct nfs_delegation *delegation = NFS_I(inode)->delegation;
787
	struct nfs4_opendata *opendata;
788
	int openflags = state->state & (FMODE_READ|FMODE_WRITE);
789
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
790
791

	if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) {
792
793
794
		ret = _nfs4_do_access(inode, sp->so_cred, openflags);
		if (ret < 0)
			return ret;
Linus Torvalds's avatar
Linus Torvalds committed
795
796
		memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid));
		set_bit(NFS_DELEGATED_STATE, &state->flags);
797
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
798
	}
799
	opendata = nfs4_opendata_alloc(dentry, sp, openflags, NULL);
800
	if (opendata == NULL)
801
802
803
804
805
806
		return -ENOMEM;
	ret = nfs4_open_recover(opendata, state);
	if (ret == -ESTALE) {
		/* Invalidate the state owner so we don't ever use it again */
		nfs4_drop_state_owner(sp);
		d_drop(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
807
	}
808
	nfs4_opendata_free(opendata);
809
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
810
811
}

812
813
814
815
816
817
818
819
820
821
822
823
824
825
static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry)
{
	struct nfs_server *server = NFS_SERVER(dentry->d_inode);
	struct nfs4_exception exception = { };
	int err;

	do {
		err = _nfs4_open_expired(sp, state, dentry);
		if (err == -NFS4ERR_DELAY)
			nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
	return err;
}

Linus Torvalds's avatar
Linus Torvalds committed
826
827
828
static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
	struct nfs_open_context *ctx;
829
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
830

831
832
833
834
835
836
	ctx = nfs4_state_find_open_context(state);
	if (IS_ERR(ctx))
		return PTR_ERR(ctx);
	ret = nfs4_do_open_expired(sp, state, ctx->dentry);
	put_nfs_open_context(ctx);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
837
838
839
}

/*
840
 * Returns a referenced nfs4_state if there is an open delegation on the file
Linus Torvalds's avatar
Linus Torvalds committed
841
842
843
844
845
846
847
848
849
850
851
852
 */
static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res)
{
	struct nfs_delegation *delegation;
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs4_client *clp = server->nfs4_state;
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs4_state_owner *sp = NULL;
	struct nfs4_state *state = NULL;
	int open_flags = flags & (FMODE_READ|FMODE_WRITE);
	int err;

853
854
855
856
857
	err = -ENOMEM;
	if (!(sp = nfs4_get_state_owner(server, cred))) {
		dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
		return err;
	}
858
859
	err = nfs4_recover_expired_lease(server);
	if (err != 0)
860
		goto out_put_state_owner;
Linus Torvalds's avatar
Linus Torvalds committed
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
	/* Protect against reboot recovery - NOTE ORDER! */
	down_read(&clp->cl_sem);
	/* Protect against delegation recall */
	down_read(&nfsi->rwsem);
	delegation = NFS_I(inode)->delegation;
	err = -ENOENT;
	if (delegation == NULL || (delegation->type & open_flags) != open_flags)
		goto out_err;
	err = -ENOMEM;
	state = nfs4_get_open_state(inode, sp);
	if (state == NULL)
		goto out_err;

	err = -ENOENT;
	if ((state->state & open_flags) == open_flags) {
		spin_lock(&inode->i_lock);
877
		update_open_stateflags(state, open_flags);
Linus Torvalds's avatar
Linus Torvalds committed
878
879
880
		spin_unlock(&inode->i_lock);
		goto out_ok;
	} else if (state->state != 0)
881
		goto out_put_open_state;
Linus Torvalds's avatar
Linus Torvalds committed
882
883
884
885
886

	lock_kernel();
	err = _nfs4_do_access(inode, cred, open_flags);
	unlock_kernel();
	if (err != 0)
887
		goto out_put_open_state;
Linus Torvalds's avatar
Linus Torvalds committed
888
889
890
891
892
893
894
	set_bit(NFS_DELEGATED_STATE, &state->flags);
	update_open_stateid(state, &delegation->stateid, open_flags);
out_ok:
	nfs4_put_state_owner(sp);
	up_read(&nfsi->rwsem);
	up_read(&clp->cl_sem);
	*res = state;
895
896
897
	return 0;
out_put_open_state:
	nfs4_put_open_state(state);
Linus Torvalds's avatar
Linus Torvalds committed
898
899
900
out_err:
	up_read(&nfsi->rwsem);
	up_read(&clp->cl_sem);
901
902
	if (err != -EACCES)
		nfs_inode_return_delegation(inode);
903
904
out_put_state_owner:
	nfs4_put_state_owner(sp);
Linus Torvalds's avatar
Linus Torvalds committed
905
906
907
908
909
910
	return err;
}

static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred)
{
	struct nfs4_exception exception = { };
911
	struct nfs4_state *res = ERR_PTR(-EIO);
Linus Torvalds's avatar
Linus Torvalds committed
912
913
914
915
916
917
918
919
920
921
922
923
924
	int err;

	do {
		err = _nfs4_open_delegated(inode, flags, cred, &res);
		if (err == 0)
			break;
		res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(inode),
					err, &exception));
	} while (exception.retry);
	return res;
}

/*
925
 * Returns a referenced nfs4_state
Linus Torvalds's avatar
Linus Torvalds committed
926
927
928
929
930
931
932
 */
static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
{
	struct nfs4_state_owner  *sp;
	struct nfs4_state     *state = NULL;
	struct nfs_server       *server = NFS_SERVER(dir);
	struct nfs4_client *clp = server->nfs4_state;
933
	struct nfs4_opendata *opendata;
Linus Torvalds's avatar
Linus Torvalds committed
934
935
936
937
938
939
940
941
	int                     status;

	/* Protect against reboot recovery conflicts */
	status = -ENOMEM;
	if (!(sp = nfs4_get_state_owner(server, cred))) {
		dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
		goto out_err;
	}
942
943
	status = nfs4_recover_expired_lease(server);
	if (status != 0)
944
		goto err_put_state_owner;
945
946
	down_read(&clp->cl_sem);
	status = -ENOMEM;
947
948
949
	opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr);
	if (opendata == NULL)
		goto err_put_state_owner;
Linus Torvalds's avatar
Linus Torvalds committed
950

951
	status = _nfs4_proc_open(opendata);
Linus Torvalds's avatar
Linus Torvalds committed
952
	if (status != 0)
953
		goto err_opendata_free;
Linus Torvalds's avatar
Linus Torvalds committed
954
955

	status = -ENOMEM;
956
957
	state = nfs4_opendata_to_nfs4_state(opendata);
	if (state == NULL)
958
959
		goto err_opendata_free;
	if (opendata->o_res.delegation_type != 0)
960
		nfs_inode_set_delegation(state->inode, cred, &opendata->o_res);
961
	nfs4_opendata_free(opendata);
Linus Torvalds's avatar
Linus Torvalds committed
962
963
964
965
	nfs4_put_state_owner(sp);
	up_read(&clp->cl_sem);
	*res = state;
	return 0;
966
967
968
969
err_opendata_free:
	nfs4_opendata_free(opendata);
err_put_state_owner:
	nfs4_put_state_owner(sp);
Linus Torvalds's avatar
Linus Torvalds committed
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
out_err:
	/* Note: clp->cl_sem must be released before nfs4_put_open_state()! */
	up_read(&clp->cl_sem);
	*res = NULL;
	return status;
}


static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred)
{
	struct nfs4_exception exception = { };
	struct nfs4_state *res;
	int status;

	do {
		status = _nfs4_do_open(dir, dentry, flags, sattr, cred, &res);
		if (status == 0)
			break;
		/* NOTE: BAD_SEQID means the server and client disagree about the
		 * book-keeping w.r.t. state-changing operations
		 * (OPEN/CLOSE/LOCK/LOCKU...)
		 * It is actually a sign of a bug on the client or on the server.
		 *
		 * If we receive a BAD_SEQID error in the particular case of
994
		 * doing an OPEN, we assume that nfs_increment_open_seqid() will
Linus Torvalds's avatar
Linus Torvalds committed
995
996
997
998
999
1000
		 * have unhashed the old state_owner for us, and that we can
		 * therefore safely retry using a new one. We should still warn
		 * the user though...
		 */
		if (status == -NFS4ERR_BAD_SEQID) {
			printk(KERN_WARNING "NFS: v4 server returned a bad sequence-id error!\n");
For faster browsing, not all history is shown. View entire blame