/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RequestBuilder.h"
#include <errno.h>
#include <string.h>
#include <util/Random.h>
#include "Cookie.h"
#include "OpenState.h"
#include "RPCCallback.h"
#include "RPCCallbackServer.h"
RequestBuilder::RequestBuilder(Procedure proc)
:
fOpCount(0),
fProcedure(proc),
fRequest(NULL)
{
_InitHeader();
}
RequestBuilder::~RequestBuilder()
{
delete fRequest;
}
void
RequestBuilder::_InitHeader()
{
fRequest = RPC::Call::Create(fProcedure, RPC::Auth::CreateSys(),
RPC::Auth::CreateNone());
if (fRequest == NULL)
return;
if (fProcedure == ProcCompound) {
fRequest->Stream().AddOpaque(NULL, 0);
fRequest->Stream().AddUInt(0);
fOpCountPosition = fRequest->Stream().Current();
fRequest->Stream().AddUInt(0);
}
}
status_t
RequestBuilder::Access()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpAccess);
fRequest->Stream().AddUInt(ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY
| ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Close(uint32 seq, const uint32* id, uint32 stateSeq)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpClose);
fRequest->Stream().AddUInt(seq);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Commit(uint64 offset, uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpCommit);
fRequest->Stream().AddUHyper(offset);
fRequest->Stream().AddUInt(count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Create(FileType type, const char* name, AttrValue* attr,
uint32 count, const char* path)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
if (type == NF4LNK && path == NULL)
return B_BAD_VALUE;
if (name == NULL)
return B_BAD_VALUE;
if (type == NF4BLK || type == NF4CHR)
return B_BAD_VALUE;
fRequest->Stream().AddUInt(OpCreate);
fRequest->Stream().AddUInt(type);
if (type == NF4LNK)
fRequest->Stream().AddString(path);
fRequest->Stream().AddString(name);
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
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)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpGetAttr);
_AttrBitmap(fRequest->Stream(), attrs, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::GetFH()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpGetFH);
fOpCount++;
return B_OK;
}
void
RequestBuilder::_GenerateLockOwner(XDR::WriteStream& stream,
OpenState* state, LockOwner* owner)
{
stream.AddUHyper(state->fClientID);
uint64 lockOwner[2];
lockOwner[0] = owner->fOwner;
lockOwner[1] = state->fInfo.fFileId;
stream.AddOpaque(lockOwner, sizeof(lockOwner));
}
status_t
RequestBuilder::Lock(OpenState* state, LockInfo* lock, uint32* sequence,
bool reclaim)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLock);
fRequest->Stream().AddInt(lock->fType);
fRequest->Stream().AddBoolean(reclaim);
fRequest->Stream().AddUHyper(lock->fStart);
fRequest->Stream().AddUHyper(lock->fLength);
if (lock->fOwner->fStateId[0] == 0 && lock->fOwner->fStateId[1] == 0
&& lock->fOwner->fStateId[2] == 0) {
fRequest->Stream().AddBoolean(true); // new lock owner
// open seq stateid
fRequest->Stream().AddUInt(*sequence);
fRequest->Stream().AddUInt(state->fStateSeq);
fRequest->Stream().AddUInt(state->fStateID[0]);
fRequest->Stream().AddUInt(state->fStateID[1]);
fRequest->Stream().AddUInt(state->fStateID[2]);
// lock seq owner
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
_GenerateLockOwner(fRequest->Stream(), state, lock->fOwner);
} else {
fRequest->Stream().AddBoolean(false); // old lock owner
(*sequence)--;
// lock stateid seq
fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
}
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LockT(LockType type, uint64 pos, uint64 len,
OpenState* state)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLockT);
fRequest->Stream().AddInt(type);
fRequest->Stream().AddUHyper(pos);
fRequest->Stream().AddUHyper(len);
fRequest->Stream().AddUHyper(state->fClientID);
uint32 owner = find_thread(NULL);
fRequest->Stream().AddOpaque(&owner, sizeof(owner));
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LockU(LockInfo* lock)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLockU);
fRequest->Stream().AddInt(lock->fType);
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
fRequest->Stream().AddUHyper(lock->fStart);
fRequest->Stream().AddUHyper(lock->fLength);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Link(const char* name)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
if (name == NULL)
return B_BAD_VALUE;
fRequest->Stream().AddUInt(OpLink);
fRequest->Stream().AddString(name);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LookUp(const char* name)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
if (name == NULL)
return B_BAD_VALUE;
fRequest->Stream().AddUInt(OpLookUp);
fRequest->Stream().AddString(name, strlen(name));
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LookUpUp()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLookUpUp);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Nverify(AttrValue* attr, uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpNverify);
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Open(OpenClaim claim, uint32 seq, uint32 access, uint64 id,
OpenCreate oc, uint64 ownerId, const char* name, AttrValue* attr,
uint32 count, bool excl, OpenDelegation delegationType)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpOpen);
fRequest->Stream().AddUInt(seq);
fRequest->Stream().AddUInt(access);
fRequest->Stream().AddUInt(0); // deny none
fRequest->Stream().AddUHyper(id);
char owner[128];
int pos = 0;
*(uint64*)(owner + pos) = ownerId;
pos += sizeof(uint64);
fRequest->Stream().AddOpaque(owner, pos);
fRequest->Stream().AddUInt(oc);
if (oc == OPEN4_CREATE) {
fRequest->Stream().AddInt(excl ? GUARDED4 : UNCHECKED4);
_EncodeAttrs(fRequest->Stream(), attr, count);
}
fRequest->Stream().AddUInt(claim);
switch (claim) {
case CLAIM_NULL:
fRequest->Stream().AddString(name, strlen(name));
break;
case CLAIM_PREVIOUS:
fRequest->Stream().AddUInt(delegationType);
break;
default:
return B_UNSUPPORTED;
}
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::OpenConfirm(uint32 seq, const uint32* id, uint32 stateSeq)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpOpenConfirm);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fRequest->Stream().AddUInt(seq);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::OpenAttrDir(bool create)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpOpenAttrDir);
fRequest->Stream().AddBoolean(create);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::PutFH(const FileHandle& fh)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpPutFH);
fRequest->Stream().AddOpaque(fh.fData, fh.fSize);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::PutRootFH()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpPutRootFH);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Read(const uint32* id, uint32 stateSeq, uint64 pos, uint32 len)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRead);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fRequest->Stream().AddUHyper(pos);
fRequest->Stream().AddUInt(len);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::ReadDir(uint64 cookie, uint64 cookieVerf, Attribute* attrs,
uint32 attrCount)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReadDir);
fRequest->Stream().AddUHyper(cookie);
fRequest->Stream().AddUHyper(cookieVerf);
// consider predicting this values basing on count or buffer size
fRequest->Stream().AddUInt(0x2000);
fRequest->Stream().AddUInt(0x8000);
_AttrBitmap(fRequest->Stream(), attrs, attrCount);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::ReadLink()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReadLink);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Remove(const char* file)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRemove);
fRequest->Stream().AddString(file);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Rename(const char* from, const char* to)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRename);
fRequest->Stream().AddString(from);
fRequest->Stream().AddString(to);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Renew(uint64 clientId)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRenew);
fRequest->Stream().AddUHyper(clientId);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::SaveFH()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSaveFH);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::SetAttr(const uint32* id, uint32 stateSeq, AttrValue* attr,
uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSetAttr);
fRequest->Stream().AddUInt(stateSeq);
if (id != NULL) {
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
} else {
fRequest->Stream().AddUInt(0);
fRequest->Stream().AddUInt(0);
fRequest->Stream().AddUInt(0);
}
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::SetClientID(RPC::Server* server)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSetClientID);
uint64 verifier = get_random<uint64>();
fRequest->Stream().AddUHyper(verifier);
status_t result = _GenerateClientId(fRequest->Stream(), server);
if (result != B_OK)
return result;
fRequest->Stream().AddUInt(0x40000000);
if (server->GetCallback() != NULL) {
ASSERT(server->GetCallback()->CBServer() != NULL);
uint32 id = server->GetCallback()->ID();
PeerAddress local = server->GetCallback()->CBServer()->LocalID();
PeerAddress servAddr = server->LocalID();
servAddr.SetPort(local.Port());
fRequest->Stream().AddString(local.ProtocolString());
char* uAddr = servAddr.UniversalAddress();
if (uAddr == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddString(uAddr);
free(uAddr);
fRequest->Stream().AddUInt(id);
} else {
fRequest->Stream().AddString("");
fRequest->Stream().AddString("");
fRequest->Stream().AddUInt(0);
}
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::_GenerateClientId(XDR::WriteStream& stream,
const RPC::Server* server)
{
char id[512] = "HAIKU:kernel:";
int pos = strlen(id);
PeerAddress local = server->LocalID();
memcpy(id + pos, server->ID().InAddr(), server->ID().InAddrSize());
pos += sizeof(server->ID().InAddrSize());
memcpy(id + pos, local.InAddr(), local.InAddrSize());
pos += sizeof(local.InAddrSize());
*(uint16*)(id + pos) = server->ID().Port();
pos += sizeof(uint16);
*(uint16*)(id + pos) = server->ID().fProtocol;
pos += sizeof(uint16);
stream.AddOpaque(id, pos);
return B_OK;
}
status_t
RequestBuilder::SetClientIDConfirm(uint64 id, uint64 ver)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSetClientIDConfirm);
fRequest->Stream().AddUHyper(id);
fRequest->Stream().AddUHyper(ver);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Verify(AttrValue* attr, uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpVerify);
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Write(const uint32* id, uint32 stateSeq, const void* buffer,
uint64 pos, uint32 len, bool stable)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpWrite);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fRequest->Stream().AddUHyper(pos);
fRequest->Stream().AddInt(stable ? FILE_SYNC4 : UNSTABLE4);
fRequest->Stream().AddOpaque(buffer, len);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::ReleaseLockOwner(OpenState* state, LockOwner* owner)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReleaseLockOwner);
_GenerateLockOwner(fRequest->Stream(), state, owner);
fOpCount++;
return B_OK;
}
RPC::Call*
RequestBuilder::Request()
{
if (fProcedure == ProcCompound)
fRequest->Stream().InsertUInt(fOpCountPosition, fOpCount);
if (fRequest == NULL || fRequest->Stream().Error() == B_OK)
return fRequest;
else
return NULL;
}
void
RequestBuilder::_AttrBitmap(XDR::WriteStream& stream, Attribute* attrs,
uint32 count)
{
// 2 is safe in NFS4, not in NFS4.1 though
uint32 bitmap[2];
memset(bitmap, 0, sizeof(bitmap));
for (uint32 i = 0; i < count; i++) {
bitmap[attrs[i] / 32] |= 1 << attrs[i] % 32;
}
uint32 bcount = bitmap[1] != 0 ? 2 : 1;
stream.AddUInt(bcount);
for (uint32 i = 0; i < bcount; i++)
stream.AddUInt(bitmap[i]);
}
void
RequestBuilder::_EncodeAttrs(XDR::WriteStream& stream, AttrValue* attr,
uint32 count)
{
if (count == 0) {
stream.AddUInt(0);
stream.AddOpaque(NULL, 0);
return;
}
Attribute* attrs
= reinterpret_cast<Attribute*>(malloc(sizeof(Attribute) * count));
for (uint32 i = 0; i < count; i++)
attrs[i] = static_cast<Attribute>(attr[i].fAttribute);
_AttrBitmap(stream, attrs, count);
free(attrs);
uint32 i = 0;
XDR::WriteStream str;
if (i < count && attr[i].fAttribute == FATTR4_TYPE) {
str.AddUInt(attr[i].fData.fValue32);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_SIZE) {
str.AddUHyper(attr[i].fData.fValue64);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_FILEHANDLE) {
FileHandle* fh = reinterpret_cast<FileHandle*>(attr[i].fData.fPointer);
str.AddOpaque(fh->fData, fh->fSize);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_FILEID) {
str.AddUHyper(attr[i].fData.fValue64);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_MODE) {
str.AddUInt(attr[i].fData.fValue32);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_OWNER) {
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_OWNER_GROUP) {
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_TIME_ACCESS_SET) {
str.AddInt(1); // SET_TO_CLIENT_TIME4
struct timespec* ts
= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
str.AddHyper(ts->tv_sec);
str.AddUInt(ts->tv_nsec);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_TIME_MODIFY_SET) {
str.AddInt(1); // SET_TO_CLIENT_TIME4
struct timespec* ts
= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
str.AddHyper(ts->tv_sec);
str.AddUInt(ts->tv_nsec);
i++;
}
stream.AddOpaque(str);
}
↑ V595 The 'fRequest' pointer was utilized before it was verified against nullptr. Check lines: 810, 812.
↑ V568 It's odd that the argument of sizeof() operator is the 'server->ID().InAddrSize()' expression.
↑ V568 It's odd that the argument of sizeof() operator is the 'local.InAddrSize()' expression.