nfs4: Add network code

Connection class supports sending and receiving RPC data using either
TCP or UDP as a transport protocol.
This commit is contained in:
Pawel Dziepak
2012-06-29 02:14:06 +02:00
parent 8fe02d0c04
commit e4977f2046
4 changed files with 354 additions and 0 deletions
@@ -0,0 +1,255 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "Connection.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <drivers/ksocket.h>
#include <util/kernel_cpp.h>
#define LAST_FRAGMENT 0x80000000
#define MAX_PACKET_SIZE 65535
KSOCKET_MODULE_DECL;
Connection::Connection(const sockaddr_in& addr, Transport proto, bool markers)
:
fSock(-1),
fUseMarkers(markers),
fProtocol(proto),
fServerAddress(addr)
{
mutex_init(&fSockLock, NULL);
}
Connection::~Connection()
{
kclosesocket(fSock);
mutex_destroy(&fSockLock);
}
status_t
Connection::_SendStream(const void* buffer, uint32 size)
{
status_t result;
uint32* buf = (uint32*)malloc(size + sizeof(uint32));
if (buf == NULL)
return B_NO_MEMORY;
buf[0] = htonl(size | LAST_FRAGMENT);
memcpy(buf + 1, buffer, size);
// More than one threads may send data and ksend is allowed to send partial
// data. Need a lock here.
uint32 sent = 0;
mutex_lock(&fSockLock);
do {
result = ksend(fSock, buf + sent, size + sizeof(uint32) - sent, 0);
sent += result;
} while (result > 0 && sent < size + sizeof(uint32));
mutex_unlock(&fSockLock);
if (result < 0) {
result = errno;
free(buf);
return result;
} else if (result == 0) {
free(buf);
return B_IO_ERROR;
}
free(buf);
return B_OK;
}
status_t
Connection::_SendPacket(const void* buffer, uint32 size)
{
// send on DGRAM sockets is atomic. No need to lock.
status_t result = ksend(fSock, buffer, size, 0);
if (result < 0)
return errno;
return B_OK;
}
status_t
Connection::_ReceiveStream(void** pbuffer, uint32* psize)
{
status_t result;
int32 size = 0;
void* buffer = NULL;
int32 record_size;
bool last_one;
do {
// There is only one listener thread per connection. No need to lock.
uint32 received = 0;
do {
result = krecv(fSock, &record_size + received,
sizeof(record_size) - received, 0);
received += result;
} while (result > 0 && received < sizeof(record_size));
if (result < 0) {
result = errno;
free(buffer);
return result;
} else if (result == 0) {
free(buffer);
return ECONNABORTED;
}
record_size = ntohl(record_size);
last_one = record_size < 0;
record_size &= LAST_FRAGMENT - 1;
void* ptr = realloc(buffer, size + record_size);
if (ptr == NULL) {
free(buffer);
return B_NO_MEMORY;
} else
buffer = ptr;
received = 0;
do {
result = krecv(fSock, (uint8*)buffer + size + received,
record_size - received, 0);
received += result;
} while (result > 0 && received < sizeof(record_size));
if (result < 0) {
result = errno;
free(buffer);
return result;
}
size += record_size;
} while (!last_one);
*pbuffer = buffer;
*psize = size;
return B_OK;
}
status_t
Connection::_ReceivePacket(void** pbuffer, uint32* psize)
{
status_t result;
int32 size = MAX_PACKET_SIZE;
void* buffer = malloc(size);
if (buffer == NULL)
return B_NO_MEMORY;
// There is only one listener thread per connection. No need to lock.
size = krecv(fSock, buffer, size, 0);
if (size < 0) {
result = errno;
free(buffer);
return result;
}
*pbuffer = buffer;
*psize = size;
return B_OK;
}
status_t
Connection::Connect(Connection **pconn, const ServerAddress& id)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(struct sockaddr_in);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(id.fAddress);
addr.sin_port = htons(id.fPort);
Connection* conn = new(std::nothrow) Connection(addr, id.fProtocol,
id.fProtocol == ProtocolTCP);
if (conn == NULL)
return B_NO_MEMORY;
status_t result = conn->_Connect();
if (result != B_OK) {
delete conn;
return result;
}
*pconn = conn;
return B_OK;
}
status_t
Connection::_Connect()
{
switch (fProtocol) {
case ProtocolTCP:
fSock = ksocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
break;
case ProtocolUDP:
fSock = ksocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
break;
default:
return B_BAD_VALUE;
}
if (fSock < 0)
return errno;
status_t result = kconnect(fSock, (struct sockaddr*)&fServerAddress,
fServerAddress.sin_len);
if (result < 0) {
result = errno;
kclosesocket(fSock);
return result;
}
return B_OK;
}
status_t
Connection::Reconnect()
{
kclosesocket(fSock);
return _Connect();
}
status_t
Connection::Init()
{
return ksocket_init();
}
status_t
Connection::CleanUp()
{
return ksocket_cleanup();
}
@@ -0,0 +1,88 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef CONNECTION_H
#define CONNECTION_H
#include <netinet/in.h>
#include <lock.h>
#include <SupportDefs.h>
enum Transport {
ProtocolTCP = 6,
ProtocolUDP = 11
};
struct ServerAddress {
uint32 fAddress;
uint16 fPort;
Transport fProtocol;
bool operator==(const ServerAddress& x);
bool operator<(const ServerAddress& x);
};
class Connection {
public:
static status_t Connect(Connection **conn,
const ServerAddress& id);
~Connection();
inline status_t Send(const void* buffer, uint32 size);
inline status_t Receive(void** buffer, uint32* size);
status_t Reconnect();
static status_t Init();
static status_t CleanUp();
private:
Connection(const sockaddr_in& addr,
Transport proto, bool markers);
status_t _Connect();
status_t _SendStream(const void* buffer, uint32 size);
status_t _SendPacket(const void* buffer, uint32 size);
status_t _ReceiveStream(void** buffer, uint32* size);
status_t _ReceivePacket(void** buffer, uint32* size);
int fSock;
mutex fSockLock;
const bool fUseMarkers;
const Transport fProtocol;
const sockaddr_in fServerAddress;
};
inline status_t
Connection::Send(const void* buffer, uint32 size)
{
if (fUseMarkers)
return _SendStream(buffer, size);
else
return _SendPacket(buffer, size);
}
inline status_t
Connection::Receive(void** buffer, uint32* size)
{
if (fUseMarkers)
return _ReceiveStream(buffer, size);
else
return _ReceivePacket(buffer, size);
}
#endif // CONNECTION_H
@@ -3,5 +3,6 @@ SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 ;
UsePrivateHeaders kernel ; UsePrivateHeaders kernel ;
KernelAddon nfs4 : KernelAddon nfs4 :
Connection.cpp
kernel_interface.cpp kernel_interface.cpp
; ;
@@ -9,6 +9,8 @@
#include <fs_interface.h> #include <fs_interface.h>
#include "Connection.h"
extern fs_volume_ops gNFSv4VolumeOps; extern fs_volume_ops gNFSv4VolumeOps;
extern fs_vnode_ops gNFSv4VnodeOps; extern fs_vnode_ops gNFSv4VnodeOps;
@@ -56,13 +58,21 @@ nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
static status_t static status_t
nfs4_std_ops(int32 op, ...) nfs4_std_ops(int32 op, ...)
{ {
status_t result;
switch (op) { switch (op) {
case B_MODULE_INIT: case B_MODULE_INIT:
dprintf("NFS4 Init\n"); dprintf("NFS4 Init\n");
result = Connection::Init();
if (result != B_OK)
return result;
return B_OK; return B_OK;
case B_MODULE_UNINIT: case B_MODULE_UNINIT:
dprintf("NFS4 Uninit\n"); dprintf("NFS4 Uninit\n");
result = Connection::CleanUp();
if (result != B_OK)
return result;
return B_OK; return B_OK;
default: default: