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:
@@ -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 ;
|
||||
|
||||
KernelAddon nfs4 :
|
||||
Connection.cpp
|
||||
kernel_interface.cpp
|
||||
;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <fs_interface.h>
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
|
||||
extern fs_volume_ops gNFSv4VolumeOps;
|
||||
extern fs_vnode_ops gNFSv4VnodeOps;
|
||||
@@ -56,13 +58,21 @@ nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
|
||||
static status_t
|
||||
nfs4_std_ops(int32 op, ...)
|
||||
{
|
||||
status_t result;
|
||||
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
dprintf("NFS4 Init\n");
|
||||
result = Connection::Init();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
return B_OK;
|
||||
|
||||
case B_MODULE_UNINIT:
|
||||
dprintf("NFS4 Uninit\n");
|
||||
result = Connection::CleanUp();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user