xref: /haiku/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 
17 static inline bigtime_t
18 RetryDelay(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 
29 bool
30 NFS4Object::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 
193 status_t
194 NFS4Object::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 
232 uint32
233 NFS4Object::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