/*
 * Copyright 2012 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Paweł Dziepak, pdziepak@quarnos.org
 */
 
 
#include "ReplyInterpreter.h"
 
#include <string.h>
 
#include <AutoDeleter.h>
#include <util/kernel_cpp.h>
 
#include "Cookie.h"
 
 
FSLocation::~FSLocation()
{
	if (fRootPath != NULL) {
		for (uint32 i = 0; fRootPath[i] != NULL; i++)
			free(const_cast<char*>(fRootPath[i]));
	}
	delete[] fRootPath;
	
	for (uint32 i = 0; i < fCount; i++)
		free(const_cast<char*>(fLocations[i]));
	delete[] fLocations;
}
 
 
FSLocations::~FSLocations()
{
	if (fRootPath != NULL) {
		for (uint32 i = 0; fRootPath[i] != NULL; i++)
			free(const_cast<char*>(fRootPath[i]));
	}
	delete[] fRootPath;
	
	delete[] fLocations;
}
 
 
AttrValue::AttrValue()
	:
	fAttribute(0),
	fFreePointer(false)
{
}
 
 
AttrValue::~AttrValue()
{
	if (fFreePointer)
		free(fData.fPointer);
	if (fAttribute == FATTR4_FS_LOCATIONS)
		delete fData.fLocations;
}
 
 
DirEntry::DirEntry()
	:
	fName(NULL),
	fAttrs(NULL),
	fAttrCount(0)
{
}
 
 
DirEntry::~DirEntry()
{
	free(const_cast<char*>(fName));
	delete[] fAttrs;
}
 
 
ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply)
	:
	fNFS4Error(NFS4_OK),
	fDecodeError(false),
	fReply(reply)
{
	if (reply != NULL)
		_ParseHeader();
}
 
 
ReplyInterpreter::~ReplyInterpreter()
{
	delete fReply;
}
 
 
void
ReplyInterpreter::_ParseHeader()
{
	fNFS4Error = fReply->Stream().GetUInt();
	fReply->Stream().GetOpaque(NULL);
	fReply->Stream().GetUInt();
}
 
 
status_t
ReplyInterpreter::Access(uint32* supported, uint32* allowed)
{
	status_t res = _OperationError(OpAccess);
	if (res != B_OK)
		return res;
 
	uint32 support = fReply->Stream().GetUInt();
	uint32 allow = fReply->Stream().GetUInt();
 
	if (supported != NULL)
		*supported = support;
	if (allowed != NULL)
		*allowed = allow;
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Close()
{
	status_t res = _OperationError(OpClose);
	if (res != B_OK)
		return res;
 
	fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Commit()
{
	status_t res = _OperationError(OpCommit);
	if (res != B_OK)
		return res;
 
	fReply->Stream().GetOpaque(NULL);
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Create(uint64* before, uint64* after, bool& atomic)
{
	status_t res = _OperationError(OpCreate);
	if (res != B_OK)
		return res;
 
	atomic = fReply->Stream().GetBoolean();
	*before = fReply->Stream().GetUHyper();
	*after = fReply->Stream().GetUHyper();
 
	uint32 count = fReply->Stream().GetUInt();
	for (uint32 i = 0; i < count; i++)
		fReply->Stream().GetUInt();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
// Bit Twiddling Hacks
// http://graphics.stanford.edu/~seander/bithacks.html
static inline uint32 CountBits(uint32 v)
{
	v = v - ((v >> 1) & 0x55555555);
	v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
	return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
}
 
 
status_t
ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
{
	status_t res = _OperationError(OpGetAttr);
	if (res != B_OK)
		return res;
 
	return _DecodeAttrs(fReply->Stream(), attrs, count);
}
 
 
status_t
ReplyInterpreter::GetFH(FileHandle* fh)
{
	status_t res = _OperationError(OpGetFH);
	if (res != B_OK)
		return res;
 
	uint32 size;
	const void* ptr = fReply->Stream().GetOpaque(&size);
	if (ptr == NULL || size > NFS4_FHSIZE)
		return B_BAD_VALUE;
 
	if (fh != NULL) {
		fh->fSize = size;
		memcpy(fh->fData, ptr, size);
	}
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Link(uint64* before, uint64* after, bool& atomic)
{
	status_t res = _OperationError(OpLink);
	if (res != B_OK)
		return res;
 
	atomic = fReply->Stream().GetBoolean();
	*before = fReply->Stream().GetUHyper();
	*after = fReply->Stream().GetUHyper();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Lock(LockInfo* linfo)
{
	status_t res = _OperationError(OpLock);
	if (res != B_OK)
		return res;
 
	linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
	linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
	linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
	linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::LockT(uint64* pos, uint64* len, LockType* type)
{
	status_t res = _OperationError(OpLockT);
	if (res != B_WOULD_BLOCK || NFS4Error() != NFS4ERR_DENIED)
		return res;
 
	*pos = fReply->Stream().GetUHyper();
	*len = fReply->Stream().GetUHyper();
	*type = static_cast<LockType>(fReply->Stream().GetInt());
 
	fReply->Stream().GetUHyper();
	fReply->Stream().GetOpaque(NULL);
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::LockU(LockInfo* linfo)
{
	status_t res = _OperationError(OpLockU);
	if (res != B_OK)
		return res;
 
	linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
	linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
	linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
	linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Open(uint32* id, uint32* seq, bool* confirm,
	OpenDelegationData* delegData, ChangeInfo* changeInfo)
{
	status_t res = _OperationError(OpOpen);
	if (res != B_OK)
		return res;
 
	*seq = fReply->Stream().GetUInt();
	id[0] = fReply->Stream().GetUInt();
	id[1] = fReply->Stream().GetUInt();
	id[2] = fReply->Stream().GetUInt();
 
	// change info
	bool atomic = fReply->Stream().GetBoolean();
	uint64 before = fReply->Stream().GetUHyper();
	uint64 after = fReply->Stream().GetUHyper();
	if (changeInfo != NULL) {
		changeInfo->fAtomic = atomic;
		changeInfo->fBefore = before;
		changeInfo->fAfter = after;
	}
 
	uint32 flags = fReply->Stream().GetUInt();
	*confirm = (flags & OPEN4_RESULT_CONFIRM) == OPEN4_RESULT_CONFIRM;
 
	// attrmask
	uint32 bcount = fReply->Stream().GetUInt();
	for (uint32 i = 0; i < bcount; i++)
		fReply->Stream().GetUInt();
 
	// delegation info
	uint32 delegation = fReply->Stream().GetUInt();
	OpenDelegationData data;
	if (delegData == NULL)
		delegData = &data;
 
	if (delegation == OPEN_DELEGATE_NONE) {
		delegData->fType = OPEN_DELEGATE_NONE;
		return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
	}
 
	delegData->fStateSeq = fReply->Stream().GetUInt();
	delegData->fStateID[0] = fReply->Stream().GetUInt();
	delegData->fStateID[1] = fReply->Stream().GetUInt();
	delegData->fStateID[2] = fReply->Stream().GetUInt();
 
	delegData->fRecall = fReply->Stream().GetBoolean();
 
	switch (delegation) {
		case OPEN_DELEGATE_READ:
			delegData->fType = OPEN_DELEGATE_READ;
			break;
		case OPEN_DELEGATE_WRITE:
			delegData->fType = OPEN_DELEGATE_WRITE;
 
			int32 limitBy = fReply->Stream().GetInt();
			if (limitBy == NFS_LIMIT_SIZE)
				delegData->fSpaceLimit = fReply->Stream().GetUHyper();
			else if (limitBy == NFS_LIMIT_BLOCKS) {
				uint32 numBlocks = fReply->Stream().GetUInt();
				delegData->fSpaceLimit = fReply->Stream().GetUInt() * numBlocks;
			}
			break;
	}
 
	// ACE data
	fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
	fReply->Stream().GetOpaque(NULL);
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::OpenConfirm(uint32* stateSeq)
{
	status_t res = _OperationError(OpOpenConfirm);
	if (res != B_OK)
		return res;
 
	*stateSeq = fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
	fReply->Stream().GetUInt();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Read(void* buffer, uint32* size, bool* eof)
{
	status_t res = _OperationError(OpRead);
	if (res != B_OK)
		return res;
 
	*eof = fReply->Stream().GetBoolean();
	const void* ptr = fReply->Stream().GetOpaque(size);
	memcpy(buffer, ptr, *size);
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
	DirEntry** dirents, uint32* _count,	bool* eof)
{
	status_t res = _OperationError(OpReadDir);
	if (res != B_OK)
		return res;
 
	*cookieVerf = fReply->Stream().GetUHyper();
 
	bool isNext;
	uint32 count = 0;
 
	// TODO: using  list instead of array would make this much more elegant
	// and efficient
	XDR::Stream::Position dataStart = fReply->Stream().Current();
	isNext = fReply->Stream().GetBoolean();
	while (isNext) {
		fReply->Stream().GetUHyper();
 
		free(fReply->Stream().GetString());
		AttrValue* values;
		uint32 attrCount;
		_DecodeAttrs(fReply->Stream(), &values,	&attrCount);
		delete[] values;
 
		count++;
 
		isNext = fReply->Stream().GetBoolean();
	}
 
	DirEntry* entries = new(std::nothrow) DirEntry[count];
	if (entries == NULL)
		return B_NO_MEMORY;
 
	count = 0;
	fReply->Stream().SetPosition(dataStart);
	isNext = fReply->Stream().GetBoolean();
	while (isNext) {
		*cookie = fReply->Stream().GetUHyper();
 
		entries[count].fName = fReply->Stream().GetString();
		_DecodeAttrs(fReply->Stream(), &entries[count].fAttrs,
			&entries[count].fAttrCount);
 
		count++;
 
		isNext = fReply->Stream().GetBoolean();
	}
	*eof = fReply->Stream().GetBoolean();
 
	*_count = count;
	*dirents = entries;
 
	if (fReply->Stream().IsEOF()) {
		delete[] entries;
		return B_BAD_VALUE;
	}
 
	return B_OK;
}
 
 
status_t
ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize)
{
	status_t res = _OperationError(OpReadLink);
	if (res != B_OK)
		return res;
 
	const void* ptr = fReply->Stream().GetOpaque(size);
	memcpy(buffer, ptr, min_c(*size, maxSize));
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Remove(uint64* before, uint64* after, bool& atomic)
{
	status_t res = _OperationError(OpRemove);
	if (res != B_OK)
		return res;
 
	atomic = fReply->Stream().GetBoolean();
	*before = fReply->Stream().GetUHyper();
	*after = fReply->Stream().GetUHyper();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Rename(uint64* fromBefore, uint64* fromAfter,
	bool& fromAtomic, uint64* toBefore, uint64* toAfter, bool& toAtomic)
{
	status_t res = _OperationError(OpRename);
	if (res != B_OK)
		return res;
 
	fromAtomic = fReply->Stream().GetBoolean();
	*fromBefore = fReply->Stream().GetUHyper();
	*fromAfter = fReply->Stream().GetUHyper();
 
	toAtomic = fReply->Stream().GetBoolean();
	*toBefore = fReply->Stream().GetUHyper();
	*toAfter = fReply->Stream().GetUHyper();
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::SetAttr()
{
	status_t res = _OperationError(OpSetAttr);
	if (res != B_OK)
		return res;
 
	uint32 bcount = fReply->Stream().GetUInt();
	for (uint32 i = 0; i < bcount; i++)
		fReply->Stream().GetUInt();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
{
	status_t res = _OperationError(OpSetClientID);
	if (res != B_OK)
		return res;
 
	*clientid = fReply->Stream().GetUHyper();
	*verifier = fReply->Stream().GetUHyper();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
status_t
ReplyInterpreter::Write(uint32* size)
{
	status_t res = _OperationError(OpWrite);
	if (res != B_OK)
		return res;
 
	*size = fReply->Stream().GetUInt();
	fReply->Stream().GetInt();
	fReply->Stream().GetUHyper();
 
	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
 
 
const char**
ReplyInterpreter::_GetPath(XDR::ReadStream& stream)
{
	uint32 count = stream.GetUInt();
	char** path = new char*[count + 1];
	if (path == NULL)
		return NULL;
 
	uint32 i;
	for (i = 0; i < count; i++) {
		path[i] = stream.GetString();
		if (path[i] == NULL)
			goto out;
	}
	path[count] = NULL;
 
	return const_cast<const char**>(path);
 
out:
	for (uint32 j = 0; j < i; j++)
		free(path[i]);
	delete[] path;
	return NULL;
}
 
 
status_t
ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
	uint32* count)
{
	uint32 bcount = fReply->Stream().GetUInt();
	uint32* bitmap = new(std::nothrow) uint32[bcount];
	if (bitmap == NULL)
		return B_NO_MEMORY;
	ArrayDeleter<uint32> _(bitmap);
 
	uint32 attr_count = 0;
	for (uint32 i = 0; i < bcount; i++) {
		bitmap[i] = str.GetUInt();
		attr_count += CountBits(bitmap[i]);
	}
 
	if (attr_count == 0) {
		*attrs = NULL;
		*count = 0;
		return B_OK;
	} else if (attr_count > FATTR4_MAXIMUM_ATTR_ID)
		return B_BAD_VALUE;
 
	uint32 size;
	const void* ptr = str.GetOpaque(&size);
	XDR::ReadStream stream(const_cast<void*>(ptr), size);
 
	AttrValue* values = new(std::nothrow) AttrValue[attr_count];
	if (values == NULL)
		return B_NO_MEMORY;
 
	uint32 current = 0;
 
	if (sIsAttrSet(FATTR4_SUPPORTED_ATTRS, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_SUPPORTED_ATTRS;
		uint32 count = stream.GetInt();
		uint32 i;
		// two uint32 are enough for NFS4, not for NFS4.1
		for (i = 0; i < min_c(count, 2); i++)
			((uint32*)&values[current].fData.fValue64)[i] = stream.GetUInt();
		for (; i < count; i++)
			stream.GetUInt();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_TYPE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_TYPE;
		values[current].fData.fValue32 = stream.GetInt();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_FH_EXPIRE_TYPE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_FH_EXPIRE_TYPE;
		values[current].fData.fValue32 = stream.GetUInt();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_CHANGE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_CHANGE;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_SIZE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_SIZE;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_FSID, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_FSID;
		values[current].fFreePointer = true;
 
		FileSystemId fsid;
		fsid.fMajor = stream.GetUHyper();
		fsid.fMinor = stream.GetUHyper();
		
		values[current].fData.fPointer = malloc(sizeof(fsid));
		memcpy(values[current].fData.fPointer, &fsid, sizeof(fsid));
		current++;
	}
 
	if (sIsAttrSet(FATTR4_LEASE_TIME, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_LEASE_TIME;
		values[current].fData.fValue32 = stream.GetUInt();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_FILEID, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_FILEID;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_FILES_FREE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_FILES_FREE;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_FILES_TOTAL, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_FILES_TOTAL;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_FS_LOCATIONS, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_FS_LOCATIONS;
 
		FSLocations* locs = new FSLocations;
		locs->fRootPath = _GetPath(stream);
		locs->fCount = stream.GetUInt();
		locs->fLocations = new FSLocation[locs->fCount];
		for (uint32 i = 0; i < locs->fCount; i++) {
			locs->fLocations[i].fRootPath = _GetPath(stream);
			locs->fLocations[i].fCount = stream.GetUInt();
			locs->fLocations[i].fLocations
				= new const char*[locs->fLocations[i].fCount];
			for (uint32 j = 0; j < locs->fLocations[i].fCount; j++)
				locs->fLocations[i].fLocations[j] = stream.GetString();
		}
		values[current].fData.fLocations = locs;
		current++;
	}
 
	if (sIsAttrSet(FATTR4_MAXREAD, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_MAXREAD;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_MAXWRITE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_MAXWRITE;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_MODE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_MODE;
		values[current].fData.fValue32 = stream.GetUInt();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_NUMLINKS, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_NUMLINKS;
		values[current].fData.fValue32 = stream.GetUInt();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_OWNER, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_OWNER;
		values[current].fFreePointer = true;
		values[current].fData.fPointer = stream.GetString();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_OWNER_GROUP, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_OWNER_GROUP;
		values[current].fFreePointer = true;
		values[current].fData.fPointer = stream.GetString();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_SPACE_FREE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_SPACE_FREE;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_SPACE_TOTAL, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_SPACE_TOTAL;
		values[current].fData.fValue64 = stream.GetUHyper();
		current++;
	}
 
	if (sIsAttrSet(FATTR4_TIME_ACCESS, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_TIME_ACCESS;
		values[current].fFreePointer = true;
 
		struct timespec ts;
		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
		ts.tv_nsec = static_cast<long>(stream.GetUInt());
		
		values[current].fData.fPointer = malloc(sizeof(ts));
		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
		current++;
	}
 
	if (sIsAttrSet(FATTR4_TIME_CREATE, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_TIME_CREATE;
		values[current].fFreePointer = true;
 
		struct timespec ts;
		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
		ts.tv_nsec = static_cast<long>(stream.GetUInt());
		
		values[current].fData.fPointer = malloc(sizeof(ts));
		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
		current++;
	}
 
	if (sIsAttrSet(FATTR4_TIME_METADATA, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_TIME_METADATA;
		values[current].fFreePointer = true;
 
		struct timespec ts;
		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
		ts.tv_nsec = static_cast<long>(stream.GetUInt());
		
		values[current].fData.fPointer = malloc(sizeof(ts));
		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
		current++;
	}
 
	if (sIsAttrSet(FATTR4_TIME_MODIFY, bitmap, bcount)) {
		values[current].fAttribute = FATTR4_TIME_MODIFY;
		values[current].fFreePointer = true;
 
		struct timespec ts;
		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
		ts.tv_nsec = static_cast<long>(stream.GetUInt());
		
		values[current].fData.fPointer = malloc(sizeof(ts));
		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
		current++;
	}
 
	*count = attr_count;
	*attrs = values;
	if (str.IsEOF()) {
		delete[] values;
		return B_BAD_VALUE;
	}
	return B_OK;
}
 
 
status_t
ReplyInterpreter::_OperationError(Opcode op)
{
	if (fDecodeError)
		return B_BAD_VALUE;
 
	if (fReply == NULL)
		return B_NOT_INITIALIZED;
 
	if (fReply->Error() != B_OK || fReply->Stream().IsEOF()) {
		fDecodeError = true;
		return fReply->Error();
	}
 
	if (fReply->Stream().GetInt() != op) {
		fDecodeError = true;
		return B_BAD_VALUE;
	}
 
	status_t result = _NFS4ErrorToHaiku(fReply->Stream().GetUInt());
	if (result != B_OK)
		fDecodeError = true;
	return result;
}
 
 
status_t
ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x)
{
	switch (x) {
		case NFS4_OK:			return B_OK;
		case NFS4ERR_PERM:		return B_PERMISSION_DENIED;
		case NFS4ERR_NOENT:		return B_ENTRY_NOT_FOUND;
		case NFS4ERR_IO:		return B_IO_ERROR;
		case NFS4ERR_NXIO:		return B_DEVICE_NOT_FOUND;
		case NFS4ERR_ACCESS:	return B_NOT_ALLOWED;
		case NFS4ERR_EXIST:		return B_FILE_EXISTS;
		case NFS4ERR_XDEV:		return B_CROSS_DEVICE_LINK;
		case NFS4ERR_NOTDIR:	return B_NOT_A_DIRECTORY;
		case NFS4ERR_ISDIR:		return B_IS_A_DIRECTORY;
		case NFS4ERR_INVAL:		return B_BAD_VALUE;
		case NFS4ERR_FBIG:		return B_FILE_TOO_LARGE;
		case NFS4ERR_NOTSUPP:	return B_UNSUPPORTED;
		case NFS4ERR_ROFS:		return B_READ_ONLY_DEVICE;
		case NFS4ERR_NAMETOOLONG:	return B_NAME_TOO_LONG;
		case NFS4ERR_NOTEMPTY:	return B_DIRECTORY_NOT_EMPTY;
		// ...
		case NFS4ERR_DELAY:
		case NFS4ERR_DENIED:
		case NFS4ERR_LOCKED:
		case NFS4ERR_GRACE:
								return B_WOULD_BLOCK;
 
		case NFS4ERR_STALE:
		case NFS4ERR_FHEXPIRED:
								return B_FILE_NOT_FOUND;
		// ...
		default:				return B_ERROR;
	}
}
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fData.