/*
 * Copyright 2012 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Paweł Dziepak, pdziepak@quarnos.org
 */
 
 
#include "OpenState.h"
 
#include <util/AutoLock.h>
 
#include "FileSystem.h"
#include "Request.h"
#include "WorkQueue.h"
 
 
OpenState::OpenState()
	:
	fOpened(false),
	fDelegation(NULL),
	fLocks(NULL),
	fLockOwners(NULL)
{
	mutex_init(&fLock, NULL);
 
	mutex_init(&fLocksLock, NULL);
	mutex_init(&fOwnerLock, NULL);
}
 
 
OpenState::~OpenState()
{
	if (fOpened)
		fFileSystem->RemoveOpenFile(this);
	Close();
 
	mutex_destroy(&fLock);
 
	mutex_destroy(&fLocksLock);
	mutex_destroy(&fOwnerLock);
}
 
 
LockOwner*
OpenState::GetLockOwner(uint32 owner)
{
	LockOwner* current = fLockOwners;
	while (current != NULL) {
		if (current->fOwner == owner)
			return current;
 
		current = current->fNext;
	}
 
	current = new LockOwner(owner);
	if (current == NULL)
		return NULL;
 
	current->fNext = fLockOwners;
	if (fLockOwners != NULL)
		fLockOwners->fPrev = current;
	fLockOwners = current;
 
	return current;
}
 
 
// Caller must hold fLocksLock
void
OpenState::AddLock(LockInfo* lock)
{
	lock->fNext = fLocks;
	fLocks = lock;
}
 
 
// Caller must hold fLocksLock
void
OpenState::RemoveLock(LockInfo* lock, LockInfo* prev)
{
	if (prev != NULL)
		prev->fNext = lock->fNext;
	else
		fLocks = lock->fNext;
}
 
 
void
OpenState::DeleteLock(LockInfo* lock)
{
	MutexLocker _(fOwnerLock);
 
	LockOwner* owner = lock->fOwner;
	delete lock;
 
	if (owner->fUseCount == 0) {
		if (owner->fPrev)
			owner->fPrev->fNext = owner->fNext;
		else
			fLockOwners = owner->fNext;
		if (owner->fNext)
			owner->fNext->fPrev = owner->fPrev;
 
		_ReleaseLockOwner(owner);
		delete owner;
	}
}
 
 
status_t
OpenState::_ReleaseLockOwner(LockOwner* owner)
{
	ASSERT(owner != NULL);
 
	uint32 attempt = 0;
	do {
		RPC::Server* server = fFileSystem->Server();
		Request request(server, fFileSystem);
		RequestBuilder& req = request.Builder();
 
		req.ReleaseLockOwner(this, owner);
 
		status_t result = request.Send();
		if (result != B_OK)
			return result;
 
		ReplyInterpreter& reply = request.Reply();
 
		if (HandleErrors(attempt, reply.NFS4Error(), server))
			continue;
 
		return reply.ReleaseLockOwner();
	} while (true);
}
 
 
status_t
OpenState::Reclaim(uint64 newClientID)
{
	if (!fOpened)
		return B_OK;
 
	MutexLocker _(fLock);
 
	if (fClientID == newClientID)
		return B_OK;
	fClientID = newClientID;
 
	_ReclaimOpen(newClientID);
	_ReclaimLocks(newClientID);
 
	return B_OK;
}
 
 
status_t
OpenState::_ReclaimOpen(uint64 newClientID)
{
	bool confirm;
	OpenDelegationData delegation;
	delegation.fType = OPEN_DELEGATE_NONE;
	delegation.fRecall = false;
 
	status_t result;
	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
	OpenDelegation delegType = fDelegation != NULL ? fDelegation->Type()
		: OPEN_DELEGATE_NONE;
	uint32 attempt = 0;
	do {
		RPC::Server* server = fFileSystem->Server();
		Request request(server, fFileSystem);
		RequestBuilder& req = request.Builder();
 
		req.PutFH(fInfo.fHandle);
		req.Open(CLAIM_PREVIOUS, sequence, sModeToAccess(fMode), newClientID,
			OPEN4_NOCREATE, fFileSystem->OpenOwner(), NULL, NULL, 0, false,
			delegType);
 
		result = request.Send();
		if (result != B_OK) {
			fFileSystem->OpenOwnerSequenceUnlock(sequence);
			return result;
		}
 
		ReplyInterpreter& reply = request.Reply();
 
		result = reply.PutFH();
		if (result == B_OK)
			sequence += IncrementSequence(reply.NFS4Error());
 
		if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
			&& HandleErrors(attempt, reply.NFS4Error(), server, NULL, NULL,
				&sequence)) {
			continue;
		}
 
		result = reply.Open(fStateID, &fStateSeq, &confirm, &delegation);
		if (result != B_OK) {
			fFileSystem->OpenOwnerSequenceUnlock(sequence);
			return result;
		}
 
		break;
	} while (true);
 
	if (fDelegation != NULL)
		fDelegation->SetData(delegation);
 
	if (delegation.fRecall) {
		DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
		args->fDelegation = fDelegation;
		args->fTruncate = false;
		gWorkQueue->EnqueueJob(DelegationRecall, args);
	}
 
	if (confirm)
		result = ConfirmOpen(fInfo.fHandle, this, &sequence);
 
 	fFileSystem->OpenOwnerSequenceUnlock(sequence);
	return result;
}
 
 
status_t
OpenState::_ReclaimLocks(uint64 newClientID)
{
	MutexLocker _(fLocksLock);
	LockInfo* linfo = fLocks;
	while (linfo != NULL) {
		MutexLocker locker(linfo->fOwner->fLock);
 
		if (linfo->fOwner->fClientId != newClientID) {
			memset(linfo->fOwner->fStateId, 0, sizeof(linfo->fOwner->fStateId));
			linfo->fOwner->fClientId = newClientID;
		}
 
		uint32 attempt = 0;
		uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
		do {
			RPC::Server* server = fFileSystem->Server();
			Request request(server, fFileSystem);
			RequestBuilder& req = request.Builder();
 
			req.PutFH(fInfo.fHandle);
			req.Lock(this, linfo, &sequence, true);
 
			status_t result = request.Send();
			if (result != B_OK) {
				fFileSystem->OpenOwnerSequenceUnlock(sequence);
				break;
			}
 
			ReplyInterpreter& reply = request.Reply();
 
			result = reply.PutFH();
			if (result == B_OK)
				sequence += IncrementSequence(reply.NFS4Error());
 
			if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
				&& reply.NFS4Error() !=  NFS4ERR_STALE_STATEID
				&& HandleErrors(attempt, reply.NFS4Error(), server, NULL, NULL,
					&sequence)) {
				continue;
			}
 
			reply.Lock(linfo);
 
			fFileSystem->OpenOwnerSequenceUnlock(sequence);
			break;
		} while (true);
		locker.Unlock();
 
		linfo = linfo->fNext;
	}
 
	return B_OK;
}
 
 
status_t
OpenState::Close()
{
	if (!fOpened)
		return B_OK;
 
	MutexLocker _(fLock);
	fOpened = false;
 
	uint32 attempt = 0;
	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
	do {
		RPC::Server* serv = fFileSystem->Server();
		Request request(serv, fFileSystem);
		RequestBuilder& req = request.Builder();
 
		req.PutFH(fInfo.fHandle);
		req.Close(sequence, fStateID, fStateSeq);
 
		status_t result = request.Send();
		if (result != B_OK) {
			fFileSystem->OpenOwnerSequenceUnlock(sequence);
			return result;
		}
 
		ReplyInterpreter& reply = request.Reply();
 
		result = reply.PutFH();
		if (result == B_OK)
			sequence += IncrementSequence(reply.NFS4Error());
 
		// RFC 3530 8.10.1. Some servers does not do anything to help client
		// recognize retried CLOSE requests so we just assume that BAD_STATEID
		// on CLOSE request is just a result of retransmission.
		if (reply.NFS4Error() == NFS4ERR_BAD_STATEID) {
			fFileSystem->OpenOwnerSequenceUnlock(sequence);
			return B_OK;
		}
 
		if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, this,
				&sequence)) {
			continue;
		}
 		fFileSystem->OpenOwnerSequenceUnlock(sequence);
 
		return reply.Close();
	} while (true);
}
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fClientID, fMode, fStateID, fStateSeq.