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 ;
|
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:
|
||||||
|
|||||||
Reference in New Issue
Block a user