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
19OpenState::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
33OpenState::~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
46LockOwner*
47OpenState::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
71void
72OpenState::AddLock(LockInfo* lock)
73{
74	lock->fNext = fLocks;
75	fLocks = lock;
76}
77
78
79// Caller must hold fLocksLock
80void
81OpenState::RemoveLock(LockInfo* lock, LockInfo* prev)
82{
83	if (prev != NULL)
84		prev->fNext = lock->fNext;
85	else
86		fLocks = lock->fNext;
87}
88
89
90void
91OpenState::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
112status_t
113OpenState::_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
139status_t
140OpenState::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
158status_t
159OpenState::_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
226status_t
227OpenState::_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
282status_t
283OpenState::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