xref: /haiku/src/add-ons/kernel/file_systems/nfs4/OpenState.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 "OpenState.h"
11 
12 #include <util/AutoLock.h>
13 
14 #include "FileSystem.h"
15 #include "Request.h"
16 #include "WorkQueue.h"
17 
18 
19 OpenState::OpenState()
20 	:
21 	fOpened(false),
22 	fDelegation(NULL),
23 	fLocks(NULL),
24 	fLockOwners(NULL)
25 {
26 	mutex_init(&fLock, NULL);
27 
28 	mutex_init(&fLocksLock, NULL);
29 	mutex_init(&fOwnerLock, NULL);
30 }
31 
32 
33 OpenState::~OpenState()
34 {
35 	if (fOpened)
36 		fFileSystem->RemoveOpenFile(this);
37 	Close();
38 
39 	mutex_destroy(&fLock);
40 
41 	mutex_destroy(&fLocksLock);
42 	mutex_destroy(&fOwnerLock);
43 }
44 
45 
46 LockOwner*
47 OpenState::GetLockOwner(uint32 owner)
48 {
49 	LockOwner* current = fLockOwners;
50 	while (current != NULL) {
51 		if (current->fOwner == owner)
52 			return current;
53 
54 		current = current->fNext;
55 	}
56 
57 	current = new LockOwner(owner);
58 	if (current == NULL)
59 		return NULL;
60 
61 	current->fNext = fLockOwners;
62 	if (fLockOwners != NULL)
63 		fLockOwners->fPrev = current;
64 	fLockOwners = current;
65 
66 	return current;
67 }
68 
69 
70 // Caller must hold fLocksLock
71 void
72 OpenState::AddLock(LockInfo* lock)
73 {
74 	lock->fNext = fLocks;
75 	fLocks = lock;
76 }
77 
78 
79 // Caller must hold fLocksLock
80 void
81 OpenState::RemoveLock(LockInfo* lock, LockInfo* prev)
82 {
83 	if (prev != NULL)
84 		prev->fNext = lock->fNext;
85 	else
86 		fLocks = lock->fNext;
87 }
88 
89 
90 void
91 OpenState::DeleteLock(LockInfo* lock)
92 {
93 	MutexLocker _(fOwnerLock);
94 
95 	LockOwner* owner = lock->fOwner;
96 	delete lock;
97 
98 	if (owner->fUseCount == 0) {
99 		if (owner->fPrev)
100 			owner->fPrev->fNext = owner->fNext;
101 		else
102 			fLockOwners = owner->fNext;
103 		if (owner->fNext)
104 			owner->fNext->fPrev = owner->fPrev;
105 
106 		_ReleaseLockOwner(owner);
107 		delete owner;
108 	}
109 }
110 
111 
112 status_t
113 OpenState::_ReleaseLockOwner(LockOwner* owner)
114 {
115 	ASSERT(owner != NULL);
116 
117 	uint32 attempt = 0;
118 	do {
119 		RPC::Server* server = fFileSystem->Server();
120 		Request request(server, fFileSystem);
121 		RequestBuilder& req = request.Builder();
122 
123 		req.ReleaseLockOwner(this, owner);
124 
125 		status_t result = request.Send();
126 		if (result != B_OK)
127 			return result;
128 
129 		ReplyInterpreter& reply = request.Reply();
130 
131 		if (HandleErrors(attempt, reply.NFS4Error(), server))
132 			continue;
133 
134 		return reply.ReleaseLockOwner();
135 	} while (true);
136 }
137 
138 
139 status_t
140 OpenState::Reclaim(uint64 newClientID)
141 {
142 	if (!fOpened)
143 		return B_OK;
144 
145 	MutexLocker _(fLock);
146 
147 	if (fClientID == newClientID)
148 		return B_OK;
149 	fClientID = newClientID;
150 
151 	_ReclaimOpen(newClientID);
152 	_ReclaimLocks(newClientID);
153 
154 	return B_OK;
155 }
156 
157 
158 status_t
159 OpenState::_ReclaimOpen(uint64 newClientID)
160 {
161 	bool confirm;
162 	OpenDelegationData delegation;
163 	delegation.fType = OPEN_DELEGATE_NONE;
164 	delegation.fRecall = false;
165 
166 	status_t result;
167 	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
168 	OpenDelegation delegType = fDelegation != NULL ? fDelegation->Type()
169 		: OPEN_DELEGATE_NONE;
170 	uint32 attempt = 0;
171 	do {
172 		RPC::Server* server = fFileSystem->Server();
173 		Request request(server, fFileSystem);
174 		RequestBuilder& req = request.Builder();
175 
176 		req.PutFH(fInfo.fHandle);
177 		req.Open(CLAIM_PREVIOUS, sequence, sModeToAccess(fMode), newClientID,
178 			OPEN4_NOCREATE, fFileSystem->OpenOwner(), NULL, NULL, 0, false,
179 			delegType);
180 
181 		result = request.Send();
182 		if (result != B_OK) {
183 			fFileSystem->OpenOwnerSequenceUnlock(sequence);
184 			return result;
185 		}
186 
187 		ReplyInterpreter& reply = request.Reply();
188 
189 		result = reply.PutFH();
190 		if (result == B_OK)
191 			sequence += IncrementSequence(reply.NFS4Error());
192 
193 		if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
194 			&& HandleErrors(attempt, reply.NFS4Error(), server, NULL, NULL,
195 				&sequence)) {
196 			continue;
197 		}
198 
199 		result = reply.Open(fStateID, &fStateSeq, &confirm, &delegation);
200 		if (result != B_OK) {
201 			fFileSystem->OpenOwnerSequenceUnlock(sequence);
202 			return result;
203 		}
204 
205 		break;
206 	} while (true);
207 
208 	if (fDelegation != NULL)
209 		fDelegation->SetData(delegation);
210 
211 	if (delegation.fRecall) {
212 		DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
213 		args->fDelegation = fDelegation;
214 		args->fTruncate = false;
215 		gWorkQueue->EnqueueJob(DelegationRecall, args);
216 	}
217 
218 	if (confirm)
219 		result = ConfirmOpen(fInfo.fHandle, this, &sequence);
220 
221  	fFileSystem->OpenOwnerSequenceUnlock(sequence);
222 	return result;
223 }
224 
225 
226 status_t
227 OpenState::_ReclaimLocks(uint64 newClientID)
228 {
229 	MutexLocker _(fLocksLock);
230 	LockInfo* linfo = fLocks;
231 	while (linfo != NULL) {
232 		MutexLocker locker(linfo->fOwner->fLock);
233 
234 		if (linfo->fOwner->fClientId != newClientID) {
235 			memset(linfo->fOwner->fStateId, 0, sizeof(linfo->fOwner->fStateId));
236 			linfo->fOwner->fClientId = newClientID;
237 		}
238 
239 		uint32 attempt = 0;
240 		uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
241 		do {
242 			RPC::Server* server = fFileSystem->Server();
243 			Request request(server, fFileSystem);
244 			RequestBuilder& req = request.Builder();
245 
246 			req.PutFH(fInfo.fHandle);
247 			req.Lock(this, linfo, &sequence, true);
248 
249 			status_t result = request.Send();
250 			if (result != B_OK) {
251 				fFileSystem->OpenOwnerSequenceUnlock(sequence);
252 				break;
253 			}
254 
255 			ReplyInterpreter& reply = request.Reply();
256 
257 			result = reply.PutFH();
258 			if (result == B_OK)
259 				sequence += IncrementSequence(reply.NFS4Error());
260 
261 			if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
262 				&& reply.NFS4Error() !=  NFS4ERR_STALE_STATEID
263 				&& HandleErrors(attempt, reply.NFS4Error(), server, NULL, NULL,
264 					&sequence)) {
265 				continue;
266 			}
267 
268 			reply.Lock(linfo);
269 
270 			fFileSystem->OpenOwnerSequenceUnlock(sequence);
271 			break;
272 		} while (true);
273 		locker.Unlock();
274 
275 		linfo = linfo->fNext;
276 	}
277 
278 	return B_OK;
279 }
280 
281 
282 status_t
283 OpenState::Close()
284 {
285 	if (!fOpened)
286 		return B_OK;
287 
288 	MutexLocker _(fLock);
289 	fOpened = false;
290 
291 	uint32 attempt = 0;
292 	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
293 	do {
294 		RPC::Server* serv = fFileSystem->Server();
295 		Request request(serv, fFileSystem);
296 		RequestBuilder& req = request.Builder();
297 
298 		req.PutFH(fInfo.fHandle);
299 		req.Close(sequence, fStateID, fStateSeq);
300 
301 		status_t result = request.Send();
302 		if (result != B_OK) {
303 			fFileSystem->OpenOwnerSequenceUnlock(sequence);
304 			return result;
305 		}
306 
307 		ReplyInterpreter& reply = request.Reply();
308 
309 		result = reply.PutFH();
310 		if (result == B_OK)
311 			sequence += IncrementSequence(reply.NFS4Error());
312 
313 		// RFC 3530 8.10.1. Some servers does not do anything to help client
314 		// recognize retried CLOSE requests so we just assume that BAD_STATEID
315 		// on CLOSE request is just a result of retransmission.
316 		if (reply.NFS4Error() == NFS4ERR_BAD_STATEID) {
317 			fFileSystem->OpenOwnerSequenceUnlock(sequence);
318 			return B_OK;
319 		}
320 
321 		if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, this,
322 				&sequence)) {
323 			continue;
324 		}
325  		fFileSystem->OpenOwnerSequenceUnlock(sequence);
326 
327 		return reply.Close();
328 	} while (true);
329 }
330 
331