1/*
2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Pawe�� Dziepak, pdziepak@quarnos.org
7 */
8
9
10#include "Cookie.h"
11#include "FileSystem.h"
12#include "NFS4Object.h"
13#include "OpenState.h"
14#include "Request.h"
15
16
17static inline bigtime_t
18RetryDelay(uint32 attempt, uint32 leaseTime = 0)
19{
20	attempt = min_c(attempt, 10);
21
22	bigtime_t delay = (bigtime_t(1) << (attempt - 1)) * 100000;
23	if (leaseTime != 0)
24		delay = min_c(delay, sSecToBigTime(leaseTime));
25	return delay;
26}
27
28
29bool
30NFS4Object::HandleErrors(uint32& attempt, uint32 nfs4Error, RPC::Server* server,
31	OpenStateCookie* cookie, OpenState* state, uint32* sequence)
32{
33	// No request send by the client should cause any of the following errors.
34	ASSERT(nfs4Error != NFS4ERR_CLID_INUSE);
35	ASSERT(nfs4Error != NFS4ERR_BAD_STATEID);
36	ASSERT(nfs4Error != NFS4ERR_RESTOREFH);
37	ASSERT(nfs4Error != NFS4ERR_LOCKS_HELD);
38	ASSERT(nfs4Error != NFS4ERR_OP_ILLEGAL);
39
40	attempt++;
41
42	if (cookie != NULL)
43		state = cookie->fOpenState;
44
45	uint32 leaseTime;
46	status_t result;
47	switch (nfs4Error) {
48		case NFS4_OK:
49			return false;
50
51		// retransmission of CLOSE caused seqid to fall back
52		case NFS4ERR_BAD_SEQID:
53			if (attempt == 1) {
54				ASSERT(sequence != NULL);
55				(*sequence)++;
56				return true;
57			}
58			return false;
59
60		// resource is locked, we need to wait
61		case NFS4ERR_DENIED:
62			if (cookie == NULL)
63				return false;
64
65			if (sequence != NULL)
66				fFileSystem->OpenOwnerSequenceUnlock(*sequence);
67
68			result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
69				B_RELATIVE_TIMEOUT, RetryDelay(attempt));
70
71			if (sequence != NULL)
72				*sequence = fFileSystem->OpenOwnerSequenceLock();
73
74			if (result != B_TIMED_OUT) {
75				if (result == B_OK)
76					release_sem(cookie->fSnoozeCancel);
77				return false;
78			}
79			return true;
80
81		// server needs more time, we need to wait
82		case NFS4ERR_LOCKED:
83		case NFS4ERR_DELAY:
84			if (sequence != NULL)
85				fFileSystem->OpenOwnerSequenceUnlock(*sequence);
86
87			if (cookie == NULL) {
88				snooze_etc(RetryDelay(attempt), B_SYSTEM_TIMEBASE,
89					B_RELATIVE_TIMEOUT);
90
91
92				if (sequence != NULL)
93					*sequence = fFileSystem->OpenOwnerSequenceLock();
94
95				return true;
96			}
97
98			if ((cookie->fMode & O_NONBLOCK) == 0) {
99				result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
100					B_RELATIVE_TIMEOUT, RetryDelay(attempt));
101
102				if (sequence != NULL)
103					*sequence = fFileSystem->OpenOwnerSequenceLock();
104
105				if (result != B_TIMED_OUT) {
106					if (result == B_OK)
107						release_sem(cookie->fSnoozeCancel);
108					return false;
109				}
110				return true;
111			}
112
113			if (sequence != NULL)
114				*sequence = fFileSystem->OpenOwnerSequenceLock();
115			return false;
116
117		// server is in grace period, we need to wait
118		case NFS4ERR_GRACE:
119			leaseTime = fFileSystem->NFSServer()->LeaseTime();
120			if (sequence != NULL)
121				fFileSystem->OpenOwnerSequenceUnlock(*sequence);
122
123			if (cookie == NULL) {
124				snooze_etc(RetryDelay(attempt, leaseTime), B_SYSTEM_TIMEBASE,
125					B_RELATIVE_TIMEOUT);
126				if (sequence != NULL)
127					*sequence = fFileSystem->OpenOwnerSequenceLock();
128				return true;
129			}
130
131			if ((cookie->fMode & O_NONBLOCK) == 0) {
132				result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
133					B_RELATIVE_TIMEOUT, RetryDelay(attempt, leaseTime));
134
135				if (sequence != NULL)
136					*sequence = fFileSystem->OpenOwnerSequenceLock();
137
138				if (result != B_TIMED_OUT) {
139					if (result == B_OK)
140						release_sem(cookie->fSnoozeCancel);
141					return false;
142				}
143				return true;
144			}
145
146			if (sequence != NULL)
147				*sequence = fFileSystem->OpenOwnerSequenceLock();
148			return false;
149
150		// server has rebooted, reclaim share and try again
151		case NFS4ERR_STALE_CLIENTID:
152		case NFS4ERR_STALE_STATEID:
153			if (state != NULL) {
154				if (sequence != NULL)
155					fFileSystem->OpenOwnerSequenceUnlock(*sequence);
156
157				fFileSystem->NFSServer()->ServerRebooted(state->fClientID);
158				if (sequence != NULL)
159					*sequence = fFileSystem->OpenOwnerSequenceLock();
160
161				return true;
162			}
163			return false;
164
165		// File Handle has expired, is invalid or the node has been deleted
166		case NFS4ERR_BADHANDLE:
167		case NFS4ERR_FHEXPIRED:
168		case NFS4ERR_STALE:
169			if (fInfo.UpdateFileHandles(fFileSystem) == B_OK)
170				return true;
171			return false;
172
173		// filesystem has been moved
174		case NFS4ERR_LEASE_MOVED:
175		case NFS4ERR_MOVED:
176			fFileSystem->Migrate(server);
177			return true;
178
179		// lease has expired
180		case NFS4ERR_EXPIRED:
181			if (state != NULL) {
182				fFileSystem->NFSServer()->ClientId(state->fClientID, true);
183				return true;
184			}
185			return false;
186
187		default:
188			return false;
189	}
190}
191
192
193status_t
194NFS4Object::ConfirmOpen(const FileHandle& fh, OpenState* state,
195	uint32* sequence)
196{
197	ASSERT(state != NULL);
198	ASSERT(sequence != NULL);
199
200	uint32 attempt = 0;
201	do {
202		RPC::Server* serv = fFileSystem->Server();
203		Request request(serv, fFileSystem);
204
205		RequestBuilder& req = request.Builder();
206
207		req.PutFH(fh);
208		req.OpenConfirm(*sequence, state->fStateID, state->fStateSeq);
209
210		status_t result = request.Send();
211		if (result != B_OK)
212			return result;
213
214		ReplyInterpreter& reply = request.Reply();
215
216		result = reply.PutFH();
217		if (result == B_OK)
218			*sequence += IncrementSequence(reply.NFS4Error());
219
220		if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state))
221			continue;
222
223		result = reply.OpenConfirm(&state->fStateSeq);
224		if (result != B_OK)
225			return result;
226
227		return B_OK;
228	} while (true);
229}
230
231
232uint32
233NFS4Object::IncrementSequence(uint32 error)
234{
235	if (error != NFS4ERR_STALE_CLIENTID && error != NFS4ERR_STALE_STATEID
236		&& error != NFS4ERR_BAD_STATEID && error != NFS4ERR_BAD_SEQID
237		&& error != NFS4ERR_BADXDR && error != NFS4ERR_RESOURCE
238		&& error != NFS4ERR_NOFILEHANDLE)
239		return 1;
240
241	return 0;
242}
243
244