diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.cpp b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp new file mode 100644 index 0000000000..23f402bdd7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp @@ -0,0 +1,255 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Connection.h" + +#include +#include +#include +#include + +#include +#include + + +#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(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.h b/src/add-ons/kernel/file_systems/nfs4/Connection.h new file mode 100644 index 0000000000..6ac07d318a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.h @@ -0,0 +1,88 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef CONNECTION_H +#define CONNECTION_H + + +#include + +#include +#include + + +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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/Jamfile b/src/add-ons/kernel/file_systems/nfs4/Jamfile index 573415bf6b..7c16aa0e5c 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Jamfile +++ b/src/add-ons/kernel/file_systems/nfs4/Jamfile @@ -3,5 +3,6 @@ SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 ; UsePrivateHeaders kernel ; KernelAddon nfs4 : + Connection.cpp kernel_interface.cpp ; diff --git a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp index e76a4b7375..391678b406 100644 --- a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp @@ -9,6 +9,8 @@ #include +#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: