* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Cookie.h"
#include "FileSystem.h"
#include "NFS4Object.h"
#include "OpenState.h"
#include "Request.h"
static inline bigtime_t
RetryDelay(uint32 attempt, uint32 leaseTime = 0)
{
attempt = min_c(attempt, 10);
bigtime_t delay = (bigtime_t(1) << (attempt - 1)) * 100000;
if (leaseTime != 0)
delay = min_c(delay, sSecToBigTime(leaseTime));
return delay;
}
bool
NFS4Object::HandleErrors(uint32& attempt, uint32 nfs4Error, RPC::Server* server,
OpenStateCookie* cookie, OpenState* state, uint32* sequence)
{
ASSERT(nfs4Error != NFS4ERR_CLID_INUSE);
ASSERT(nfs4Error != NFS4ERR_BAD_STATEID);
ASSERT(nfs4Error != NFS4ERR_RESTOREFH);
ASSERT(nfs4Error != NFS4ERR_LOCKS_HELD);
ASSERT(nfs4Error != NFS4ERR_OP_ILLEGAL);
attempt++;
if (cookie != NULL)
state = cookie->fOpenState;
uint32 leaseTime;
status_t result;
switch (nfs4Error) {
case NFS4_OK:
return false;
case NFS4ERR_BAD_SEQID:
if (attempt == 1) {
ASSERT(sequence != NULL);
(*sequence)++;
return true;
}
return false;
case NFS4ERR_DENIED:
if (cookie == NULL)
return false;
if (sequence != NULL)
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
B_RELATIVE_TIMEOUT, RetryDelay(attempt));
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
if (result != B_TIMED_OUT) {
if (result == B_OK)
release_sem(cookie->fSnoozeCancel);
return false;
}
return true;
case NFS4ERR_LOCKED:
case NFS4ERR_DELAY:
if (sequence != NULL)
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
if (cookie == NULL) {
snooze_etc(RetryDelay(attempt), B_SYSTEM_TIMEBASE,
B_RELATIVE_TIMEOUT);
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return true;
}
if ((cookie->fMode & O_NONBLOCK) == 0) {
result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
B_RELATIVE_TIMEOUT, RetryDelay(attempt));
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
if (result != B_TIMED_OUT) {
if (result == B_OK)
release_sem(cookie->fSnoozeCancel);
return false;
}
return true;
}
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return false;
case NFS4ERR_GRACE:
leaseTime = fFileSystem->NFSServer()->LeaseTime();
if (sequence != NULL)
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
if (cookie == NULL) {
snooze_etc(RetryDelay(attempt, leaseTime), B_SYSTEM_TIMEBASE,
B_RELATIVE_TIMEOUT);
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return true;
}
if ((cookie->fMode & O_NONBLOCK) == 0) {
result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
B_RELATIVE_TIMEOUT, RetryDelay(attempt, leaseTime));
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
if (result != B_TIMED_OUT) {
if (result == B_OK)
release_sem(cookie->fSnoozeCancel);
return false;
}
return true;
}
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return false;
case NFS4ERR_STALE_CLIENTID:
case NFS4ERR_STALE_STATEID:
if (state != NULL) {
if (sequence != NULL)
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
fFileSystem->NFSServer()->ServerRebooted(state->fClientID);
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return true;
}
return false;
case NFS4ERR_BADHANDLE:
case NFS4ERR_FHEXPIRED:
case NFS4ERR_STALE:
if (fInfo.UpdateFileHandles(fFileSystem) == B_OK)
return true;
return false;
case NFS4ERR_LEASE_MOVED:
case NFS4ERR_MOVED:
fFileSystem->Migrate(server);
return true;
case NFS4ERR_EXPIRED:
if (state != NULL) {
fFileSystem->NFSServer()->ClientId(state->fClientID, true);
return true;
}
return false;
case NFS4ERR_OPENMODE:
if (state->fDelegation != NULL)
return true;
return false;
default:
return false;
}
}
status_t
NFS4Object::ConfirmOpen(const FileHandle& fh, OpenState* state,
uint32* sequence)
{
ASSERT(state != NULL);
ASSERT(sequence != NULL);
uint32 attempt = 0;
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem, geteuid(), getegid());
RequestBuilder& req = request.Builder();
req.PutFH(fh);
req.OpenConfirm(*sequence, state->fStateID, state->fStateSeq);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
result = reply.PutFH();
if (result == B_OK)
*sequence += IncrementSequence(reply.NFS4Error());
if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state))
continue;
result = reply.OpenConfirm(&state->fStateSeq);
if (result != B_OK)
return result;
return B_OK;
} while (true);
}
uint32
NFS4Object::IncrementSequence(uint32 error)
{
if (error != NFS4ERR_STALE_CLIENTID && error != NFS4ERR_STALE_STATEID
&& error != NFS4ERR_BAD_STATEID && error != NFS4ERR_BAD_SEQID
&& error != NFS4ERR_BADXDR && error != NFS4ERR_RESOURCE
&& error != NFS4ERR_NOFILEHANDLE)
return 1;
return 0;
}