nfs4: Return delegations when asked to

This commit is contained in:
Pawel Dziepak
2012-08-06 02:03:28 +02:00
parent e743e24320
commit 4a15375310
25 changed files with 583 additions and 13 deletions
@@ -0,0 +1,70 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "Delegation.h"
#include "Inode.h"
#include "Request.h"
Delegation::Delegation(const OpenDelegationData& data, Inode* inode,
uint64 clientID)
:
fClientID(clientID),
fData(data),
fInode(inode)
{
rw_lock_init(&fLock, NULL);
}
Delegation::~Delegation()
{
rw_lock_destroy(&fLock);
}
status_t
Delegation::GiveUp(bool truncate)
{
if (!truncate) {
// save buffers
}
ReturnDelegation();
return B_OK;
}
status_t
Delegation::ReturnDelegation()
{
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.DelegReturn(fData.fStateID, fData.fStateSeq);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
return reply.DelegReturn();
} while (true);
}
@@ -15,9 +15,12 @@
#include "NFS4Object.h"
class Delegation : public NFS4Object {
class Inode;
class Delegation : public NFS4Object,
public DoublyLinkedListLinkImpl<Delegation> {
public:
Delegation(OpenDelegationData data, Inode* inode,
Delegation(const OpenDelegationData& data, Inode* inode,
uint64 clientID);
~Delegation();
@@ -28,7 +31,12 @@ public:
status_t Reclaim(uint64 newClientID);
status_t GiveUp(bool flush);
status_t GiveUp(bool truncate);
inline Inode* GetInode();
protected:
status_t ReturnDelegation();
private:
uint64 fClientID;
@@ -39,5 +47,12 @@ private:
};
inline Inode*
Delegation::GetInode()
{
return fInode;
}
#endif // DELEGATION_H
@@ -24,6 +24,9 @@ struct FileHandle {
inline FileHandle();
inline FileHandle(const FileHandle& fh);
inline FileHandle& operator=(const FileHandle& fh);
inline bool operator>(const FileHandle& handle) const;
inline bool operator<(const FileHandle& handle) const;
};
@@ -87,6 +90,24 @@ FileHandle::operator=(const FileHandle& fh)
}
inline bool
FileHandle::operator>(const FileHandle& handle) const
{
if (fSize > handle.fSize)
return true;
return memcmp(fData, handle.fData, fSize) > 0;
}
inline bool
FileHandle::operator<(const FileHandle& handle) const
{
if (fSize < handle.fSize)
return true;
return memcmp(fData, handle.fData, fSize) < 0;
}
inline
FileInfo::FileInfo()
:
@@ -38,6 +38,7 @@ FileSystem::FileSystem()
mutex_init(&fOpenOwnerLock, NULL);
mutex_init(&fOpenLock, NULL);
mutex_init(&fDelegationLock, NULL);
}
@@ -45,6 +46,7 @@ FileSystem::~FileSystem()
{
NFSServer()->RemoveFileSystem(this);
mutex_destroy(&fDelegationLock);
mutex_destroy(&fOpenLock);
mutex_destroy(&fOpenOwnerLock);
@@ -308,3 +310,43 @@ FileSystem::RemoveOpenFile(OpenFileCookie* cookie)
NFSServer()->DecUsage();
}
void
FileSystem::AddDelegation(Delegation* delegation)
{
MutexLocker _(fDelegationLock);
fOpenDelegations.InsertBefore(fOpenDelegations.Head(), delegation);
fHandleToDelegation.Remove(delegation->fInfo.fHandle);
fHandleToDelegation.Insert(delegation->fInfo.fHandle, delegation);
NFSServer()->IncUsage();
}
void
FileSystem::RemoveDelegation(Delegation* delegation)
{
MutexLocker _(fDelegationLock);
fOpenDelegations.Remove(delegation);
fHandleToDelegation.Remove(delegation->fInfo.fHandle);
NFSServer()->DecUsage();
}
Delegation*
FileSystem::GetDelegation(const FileHandle& handle)
{
MutexLocker _(fDelegationLock);
AVLTreeMap<FileHandle, Delegation*>::Iterator it;
it = fHandleToDelegation.Find(handle);
if (!it.HasCurrent())
return NULL;
return it.Current();
}
@@ -10,6 +10,7 @@
#include "CacheRevalidator.h"
#include "Delegation.h"
#include "InodeIdMap.h"
#include "NFS4Defs.h"
#include "NFS4Server.h"
@@ -35,6 +36,12 @@ public:
void AddOpenFile(OpenFileCookie* cookie);
void RemoveOpenFile(OpenFileCookie* cookie);
OpenFileCookie* DelegationsLock();
void DelegationsUnlock();
void AddDelegation(Delegation* delegation);
void RemoveDelegation(Delegation* delegation);
Delegation* GetDelegation(const FileHandle& handle);
inline CacheRevalidator& Revalidator();
inline bool IsAttrSupported(Attribute attr) const;
@@ -62,6 +69,10 @@ private:
CacheRevalidator fCacheRevalidator;
DoublyLinkedList<Delegation> fOpenDelegations;
mutex fDelegationLock;
AVLTreeMap<FileHandle, Delegation*> fHandleToDelegation;
OpenFileCookie* fOpenFiles;
uint32 fOpenCount;
mutex fOpenLock;
@@ -23,11 +23,13 @@
Inode::Inode()
:
fCache(NULL),
fDelegation(NULL),
fFileCache(NULL),
fMaxFileSize(0),
fOpenState(NULL),
fWriteDirty(false)
{
rw_lock_init(&fDelegationLock, NULL);
mutex_init(&fStateLock, NULL);
mutex_init(&fFileCacheLock, NULL);
}
@@ -121,12 +123,16 @@ Inode::CreateInode(FileSystem* fs, const FileInfo &fi, Inode** _inode)
Inode::~Inode()
{
if (fDelegation != NULL)
RecallDelegation();
if (fFileCache != NULL)
file_cache_delete(fFileCache);
delete fCache;
mutex_destroy(&fStateLock);
mutex_destroy(&fFileCacheLock);
rw_lock_destroy(&fDelegationLock);
}
@@ -721,3 +727,26 @@ Inode::ChildAdded(const char* name, uint64 fileID,
return fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID));
}
void
Inode::SetDelegation(Delegation* delegation)
{
WriteLocker _(fDelegationLock);
fDelegation = delegation;
fFileSystem->AddDelegation(delegation);
}
void
Inode::RecallDelegation(bool truncate)
{
WriteLocker _(fDelegationLock);
if (fDelegation == NULL)
return;
fDelegation->GiveUp(truncate);
fFileSystem->RemoveDelegation(fDelegation);
delete fDelegation;
fDelegation = NULL;
}
@@ -34,6 +34,9 @@ public:
inline uint64 MaxFileSize();
void SetDelegation(Delegation* delegation);
void RecallDelegation(bool truncate = false);
status_t LookUp(const char* name, ino_t* id);
status_t Access(int mode);
@@ -96,6 +96,8 @@ Inode::Open(int mode, OpenFileCookie* cookie)
{
MutexLocker _(fStateLock);
OpenDelegationData data;
data.fType = OPEN_DELEGATE_NONE;
if (fOpenState == NULL) {
OpenState* state = new OpenState;
if (state == NULL)
@@ -103,7 +105,7 @@ Inode::Open(int mode, OpenFileCookie* cookie)
state->fInfo = fInfo;
state->fFileSystem = fFileSystem;
status_t result = OpenFile(state, mode, NULL);
status_t result = OpenFile(state, mode, &data);
if (result != B_OK)
return result;
@@ -112,7 +114,7 @@ Inode::Open(int mode, OpenFileCookie* cookie)
int newMode = mode & O_RWMASK;
int oldMode = fOpenState->fMode & O_RWMASK;
if (oldMode != newMode && oldMode != O_RDWR) {
status_t result = OpenFile(fOpenState, O_RDWR, NULL);
status_t result = OpenFile(fOpenState, O_RDWR, &data);
if (result != B_OK)
return result;
fOpenState->fMode = O_RDWR;
@@ -128,6 +130,16 @@ Inode::Open(int mode, OpenFileCookie* cookie)
fFileSystem->AddOpenFile(cookie);
if (data.fType != OPEN_DELEGATE_NONE) {
Delegation* delegation
= new(std::nothrow) Delegation(data, this, fOpenState->fClientID);
if (delegation != NULL) {
delegation->fInfo = fOpenState->fInfo;
delegation->fFileSystem = fFileSystem;
SetDelegation(delegation);
}
}
return B_OK;
}
@@ -7,6 +7,7 @@ KernelAddon nfs4 :
CacheRevalidator.cpp
Cookie.cpp
Connection.cpp
Delegation.cpp
DirectoryCache.cpp
FileInfo.cpp
FileSystem.cpp
@@ -20,9 +21,11 @@ KernelAddon nfs4 :
NFS4Object.cpp
NFS4Server.cpp
OpenState.cpp
ReplyBuilder.cpp
ReplyInterpreter.cpp
Request.cpp
RequestBuilder.cpp
RequestInterpreter.cpp
RootInode.cpp
RPCAuth.cpp
RPCCall.cpp
@@ -25,11 +25,16 @@ enum CallbackProcedure {
CallbackProcCompound = 1
};
enum CallbackOpcode {
OpCallbackRecall = 4
};
enum Opcode {
OpAccess = 3,
OpClose = 4,
OpCommit = 5,
OpCreate = 6,
OpDelegReturn = 8,
OpGetAttr = 9,
OpGetFH = 10,
OpLink = 11,
@@ -315,3 +315,71 @@ NFS4Server::_RenewalThreadStart(void* ptr)
return server->_Renewal();
}
status_t
NFS4Server::ProcessCallback(RPC::CallbackRequest* request,
Connection* connection)
{
RequestInterpreter req(request);
ReplyBuilder reply(request->XID());
status_t result;
uint32 count = req.OperationCount();
for (uint32 i = 0; i < count; i++) {
switch (req.Operation()) {
case OpCallbackRecall:
result = CallbackRecall(&req, &reply);
break;
default:
result = B_NOT_SUPPORTED;
}
if (result != B_OK)
break;
}
XDR::WriteStream& stream = reply.Reply()->Stream();
connection->Send(stream.Buffer(), stream.Size());
return B_OK;
}
status_t
NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply)
{
uint32 stateID[3];
uint32 stateSeq;
bool truncate;
FileHandle handle;
status_t result = request->Recall(&handle, truncate, &stateSeq, stateID);
if (result != B_OK)
return result;
MutexLocker locker(fFSLock);
Delegation* delegation = NULL;
FileSystem* current = fFileSystems;
while (current != NULL) {
delegation = current->GetDelegation(handle);
if (delegation != NULL)
break;
current = current->fNext;
}
locker.Unlock();
if (delegation == NULL) {
reply->Recall(B_FILE_NOT_FOUND);
return B_FILE_NOT_FOUND;
}
// TODO: should be asynchronous
delegation->GetInode()->RecallDelegation(truncate);
reply->Recall(B_OK);
return B_OK;
}
@@ -11,6 +11,8 @@
#include <lock.h>
#include "ReplyBuilder.h"
#include "RequestInterpreter.h"
#include "RPCServer.h"
@@ -34,6 +36,12 @@ public:
uint64 ClientId(uint64 prevId = 0, bool forceNew = false);
inline uint32 LeaseTime();
virtual status_t ProcessCallback(RPC::CallbackRequest* request,
Connection* connection);
status_t CallbackRecall(RequestInterpreter* request,
ReplyBuilder* reply);
private:
status_t _ReclaimOpen(OpenFileCookie* cookie);
status_t _ReclaimLocks(OpenFileCookie* cookie);
@@ -10,15 +10,22 @@
#include "RPCCallback.h"
#include "RPCCallbackRequest.h"
#include "RPCServer.h"
using namespace RPC;
Callback::Callback(Server* server)
:
fServer(server)
{
}
status_t
Callback::EnqueueRequest(CallbackRequest* request, Connection* connection)
{
dprintf("GOT A CALLBACK REQUEST %x\n", (int)request->XID());
return B_OK;
return fServer->PrivateData()->ProcessCallback(request, connection);
}
@@ -15,9 +15,12 @@
namespace RPC {
class CallbackRequest;
class Server;
class Callback {
public:
Callback(Server* server);
inline void SetID(int32 id);
inline int32 ID();
@@ -25,6 +28,7 @@ public:
Connection* connection);
private:
Server* fServer;
int32 fID;
};
@@ -45,7 +45,10 @@ CallbackRequest::CallbackRequest(void *buffer, int size)
fProcedure = fStream.GetUInt();
fStream.GetUInt();
fStream.GetOpaque(NULL);
fStream.GetUInt();
fStream.GetOpaque(NULL);
if (fProcedure == CallbackProcCompound) {
@@ -54,6 +57,9 @@ CallbackRequest::CallbackRequest(void *buffer, int size)
return;
fID = fStream.GetUInt();
fRPCError = SUCCESS;
fError = B_OK;
} else if (fProcedure == CallbackProcNull) {
fRPCError = SUCCESS;
fError = B_OK;
@@ -26,7 +26,7 @@ CallbackServer::CallbackServer()
fConnectionList(NULL),
fListener(NULL),
fThreadRunning(false),
fCallbackArray(NULL),
//fCallbackArray(NULL),
fArraySize(0),
fFreeSlot(-1)
{
@@ -40,7 +40,7 @@ CallbackServer::~CallbackServer()
{
StopServer();
free(fCallbackArray);
//free(fCallbackArray);
rw_lock_destroy(&fArrayLock);
mutex_destroy(&fThreadLock);
mutex_destroy(&fConnectionLock);
@@ -68,7 +68,7 @@ CallbackServer::RegisterCallback(Callback* callback)
for (uint32 i = fArraySize; i < newSize; i++)
array[i].fNext = i + 1;
array[fArraySize * 2 - 1].fNext = -1;
array[newSize - 1].fNext = -1;
fCallbackArray = array;
fFreeSlot = fArraySize;
@@ -240,7 +240,7 @@ CallbackServer::ConnectionThread(ConnectionEntry* entry)
if (request == NULL || request->Error() != B_OK) {
free(buffer);
continue;
} else if (request != NULL) {
} else if (request != NULL && request->Error() != B_OK) {
reply = CallbackReply::Create(request->XID(), request->RPCError());
if (reply != NULL) {
connection->Send(reply->Stream().Buffer(),
@@ -251,7 +251,6 @@ CallbackServer::ConnectionThread(ConnectionEntry* entry)
continue;
}
switch (request->Procedure()) {
case CallbackProcCompound:
GetCallback(request->ID())->EnqueueRequest(request, connection);
@@ -228,7 +228,7 @@ Server::GetCallback()
{
MutexLocker _(fCallbackLock);
if (fCallback == NULL) {
fCallback = new Callback;
fCallback = new(std::nothrow) Callback(this);
gRPCCallbackServer->RegisterCallback(fCallback);
}
@@ -48,6 +48,9 @@ private:
class ProgramData {
public:
virtual status_t ProcessCallback(CallbackRequest* request,
Connection* connection) = 0;
virtual ~ProgramData() { }
};
@@ -0,0 +1,85 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "ReplyBuilder.h"
#include "NFS4Defs.h"
#include "RPCCallbackReply.h"
ReplyBuilder::ReplyBuilder(uint32 xid)
:
fStatus(B_OK),
fOpCount(0),
fReply(RPC::CallbackReply::Create(xid))
{
_InitHeader();
}
ReplyBuilder::~ReplyBuilder()
{
delete fReply;
}
void
ReplyBuilder::_InitHeader()
{
fStatusPosition = fReply->Stream().Current();
fReply->Stream().AddUInt(0);
fReply->Stream().AddOpaque(NULL, 0);
fOpCountPosition = fReply->Stream().Current();
fReply->Stream().AddUInt(0);
}
RPC::CallbackReply*
ReplyBuilder::Reply()
{
fReply->Stream().InsertUInt(fStatusPosition, _HaikuErrorToNFS4(fStatus));
fReply->Stream().InsertUInt(fOpCountPosition, fOpCount);
if (fReply == NULL || fReply->Stream().Error() == B_OK)
return fReply;
else
return NULL;
}
status_t
ReplyBuilder::Recall(status_t status)
{
if (fStatus != B_OK)
return B_ERROR;
fReply->Stream().AddUInt(OpCallbackRecall);
fReply->Stream().AddUInt(_HaikuErrorToNFS4(fStatus));
fStatus = status;
fOpCount++;
return B_OK;
}
uint32
ReplyBuilder::_HaikuErrorToNFS4(status_t error)
{
switch (error) {
case B_OK: return NFS4_OK;
case B_FILE_NOT_FOUND: return NFS4ERR_BADHANDLE;
case B_NOT_SUPPORTED: return NFS4ERR_OP_ILLEGAL;
default: return NFS4ERR_RESOURCE;
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef REPLYBUILDER_H
#define REPLYBUILDER_H
#include <SupportDefs.h>
#include "RPCCallbackReply.h"
#include "XDR.h"
class ReplyBuilder {
public:
ReplyBuilder(uint32 xid);
~ReplyBuilder();
RPC::CallbackReply* Reply();
status_t Recall(status_t status);
private:
void _InitHeader();
static uint32 _HaikuErrorToNFS4(status_t error);
status_t fStatus;
XDR::Stream::Position fStatusPosition;
uint32 fOpCount;
XDR::Stream::Position fOpCountPosition;
RPC::CallbackReply* fReply;
};
#endif // REPLYBUILDER_H
@@ -71,6 +71,7 @@ public:
status_t Close();
status_t Commit();
status_t Create(uint64* before, uint64* after, bool& atomic);
inline status_t DelegReturn();
status_t GetAttr(AttrValue** attrs, uint32* count);
status_t GetFH(FileHandle* fh);
status_t Link(uint64* before, uint64* after, bool& atomic);
@@ -150,6 +151,13 @@ ReplyInterpreter::NFS4Error()
}
inline status_t
ReplyInterpreter::DelegReturn()
{
return _OperationError(OpDelegReturn);
}
inline status_t
ReplyInterpreter::LookUp()
{
@@ -137,6 +137,27 @@ RequestBuilder::Create(FileType type, const char* name, AttrValue* attr,
}
status_t
RequestBuilder::DelegReturn(const uint32* id, uint32 seq)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpDelegReturn);
fRequest->Stream().AddUInt(seq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::GetAttr(Attribute* attrs, uint32 count)
{
@@ -37,6 +37,7 @@ public:
status_t Create(FileType type, const char* name,
AttrValue* attr, uint32 count,
const char* path = NULL);
status_t DelegReturn(const uint32* id, uint32 seq);
status_t GetAttr(Attribute* attrs, uint32 count);
status_t GetFH();
status_t Link(const char* name);
@@ -0,0 +1,52 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RequestInterpreter.h"
#include <string.h>
#include <util/kernel_cpp.h>
RequestInterpreter::RequestInterpreter(RPC::CallbackRequest* request)
:
fRequest(request)
{
fOperationCount = fRequest->Stream().GetUInt();
}
RequestInterpreter::~RequestInterpreter()
{
delete fRequest;
}
status_t
RequestInterpreter::Recall(FileHandle* handle, bool& truncate, uint32* stateSeq,
uint32* stateID)
{
if (fLastOperation != OpCallbackRecall)
return B_BAD_VALUE;
*stateSeq = fRequest->Stream().GetUInt();
stateID[0] = fRequest->Stream().GetUInt();
stateID[1] = fRequest->Stream().GetUInt();
stateID[2] = fRequest->Stream().GetUInt();
truncate = fRequest->Stream().GetBoolean();
uint32 size;
const void* ptr = fRequest->Stream().GetOpaque(&size);
handle->fSize = size;
memcpy(handle->fData, ptr, size);
return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
@@ -0,0 +1,54 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef REQUESTINTERPRETER_H
#define REQUESTINTERPRETER_H
#include <SupportDefs.h>
#include "FileInfo.h"
#include "NFS4Defs.h"
#include "RPCCallbackRequest.h"
class RequestInterpreter {
public:
RequestInterpreter(RPC::CallbackRequest* request);
~RequestInterpreter();
inline uint32 OperationCount();
inline uint32 Operation();
status_t Recall(FileHandle* handle, bool& truncate,
uint32* stateSeq, uint32* stateID);
private:
uint32 fOperationCount;
uint32 fLastOperation;
RPC::CallbackRequest* fRequest;
};
inline uint32
RequestInterpreter::OperationCount()
{
return fOperationCount;
}
inline uint32
RequestInterpreter::Operation()
{
fLastOperation = fRequest->Stream().GetUInt();
return fLastOperation;
}
#endif // REQUESTINTERPRETER_H