Merge branch 'nfs4'

Conflicts:
	build/jam/HaikuImage
This commit is contained in:
Pawel Dziepak
2013-03-11 13:00:55 +01:00
90 changed files with 17325 additions and 11 deletions
+2
View File
@@ -1338,6 +1338,7 @@ if $(HAIKU_NO_WERROR) != 1 {
EnableWerror src add-ons kernel file_systems layers ;
EnableWerror src add-ons kernel file_systems netfs ;
EnableWerror src add-ons kernel file_systems nfs ;
EnableWerror src add-ons kernel file_systems nfs4 ;
# EnableWerror src add-ons kernel file_systems ntfs ;
EnableWerror src add-ons kernel file_systems packagefs ;
EnableWerror src add-ons kernel file_systems ramfs ;
@@ -1347,6 +1348,7 @@ if $(HAIKU_NO_WERROR) != 1 {
EnableWerror src add-ons kernel generic ;
# EnableWerror src add-ons kernel network datalink_protocols ;
EnableWerror src add-ons kernel network devices ;
EnableWerror src add-ons kernel network dns_resolver ;
EnableWerror src add-ons kernel network notifications ;
EnableWerror src add-ons kernel network ppp ;
EnableWerror src add-ons kernel network protocols ;
+7 -6
View File
@@ -18,7 +18,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
hd head hey hostname
id ident ifconfig <bin>install installsound iroster isvolume
ideinfo@ide idestatus@ide
join kernel_debugger keymap keystore kill
join kernel_debugger keymap kill
less lessecho lesskey link linkcatkeys listarea listattr listimage listdev
listport listres listsem listusb ln locale locate logger login logname ls
lsindex
@@ -84,10 +84,10 @@ PRIVATE_SYSTEM_LIBS = [ FFilterByBuildFeatures
libilmimf.so
] ;
SYSTEM_SERVERS = [ FFilterByBuildFeatures
app_server cddb_daemon debug_server input_server keystore_server mail_daemon
app_server cddb_daemon debug_server input_server mail_daemon
media_addon_server media_server midi_server mount_server net_server
notification_server power_daemon print_server print_addon_server registrar
syslog_daemon
syslog_daemon dns_resolver_server nfs4_idmapper_server
] ;
SYSTEM_NETWORK_DEVICES = ethernet loopback ;
@@ -183,7 +183,7 @@ SYSTEM_ADD_ONS_BUS_MANAGERS = [ FFilterByBuildFeatures
ata@ata pci ps2@x86 isa@x86
ide@ide scsi config_manager agp_gart usb firewire acpi@x86
] ;
SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs btrfs cdda exfat ext2 fat iso9660 nfs
SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs btrfs cdda exfat ext2 fat iso9660 nfs nfs4
attribute_overlay write_overlay ntfs reiserfs udf googlefs ;
# wifi firmware
@@ -232,7 +232,7 @@ AddFilesToHaikuImage system add-ons kernel file_systems
: $(SYSTEM_ADD_ONS_FILE_SYSTEMS) ;
AddFilesToHaikuImage system add-ons kernel generic
: ata_adapter@ata bios@x86 dpc ide_adapter@ide
locked_pool mpu401 scsi_periph <module>tty cpuidle@x86 ;
locked_pool mpu401 scsi_periph <module>tty ; #cpuidle@x86 ;
AddFilesToHaikuImage system add-ons kernel partitioning_systems
: amiga_rdb apple efi_gpt intel session ;
AddFilesToHaikuImage system add-ons kernel interrupt_controllers
@@ -246,7 +246,7 @@ if $(TARGET_ARCH) = x86 {
AddNewDriversToHaikuImage disk scsi : scsi_cd scsi_disk ;
AddNewDriversToHaikuImage power : enhanced_speedstep@x86 ;
AddNewDriversToHaikuImage power : acpi_battery@x86 ;
AddNewDriversToHaikuImage power : x86_cpuidle@x86 ;
#AddNewDriversToHaikuImage power : x86_cpuidle@x86 ;
#AddNewDriversToHaikuImage display : display_controls@x86 ;
# legacy drivers
@@ -663,6 +663,7 @@ AddFilesToHaikuImage system add-ons input_server filters
: screen_saver shortcut_catcher ;
AddFilesToHaikuImage system add-ons kernel network
: <net>notifications stack ;
AddFilesToHaikuImage system add-ons kernel network : dns_resolver ;
AddFilesToHaikuImage system add-ons kernel network devices
: $(SYSTEM_NETWORK_DEVICES) ;
AddFilesToHaikuImage system add-ons kernel network datalink_protocols
+8
View File
@@ -242,6 +242,14 @@ struct fs_vnode_ops {
fs_vnode* _superVnode, ino_t* _nodeID);
status_t (*get_super_vnode)(fs_volume* volume, fs_vnode* vnode,
fs_volume* superVolume, fs_vnode* superVnode);
/* lock operations */
status_t (*test_lock)(fs_volume* volume, fs_vnode* vnode, void* cookie,
struct flock* lock);
status_t (*acquire_lock)(fs_volume* volume, fs_vnode* vnode, void* cookie,
const struct flock* lock, bool wait);
status_t (*release_lock)(fs_volume* volume, fs_vnode* vnode, void* cookie,
const struct flock* lock);
};
struct file_system_module_info {
+1
View File
@@ -269,6 +269,7 @@ _AVL_TREE_MAP_CLASS_NAME::MakeEmpty()
{
AVLTreeNode* root = fTree.Root();
_FreeTree(root);
fTree.MakeEmpty();
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef DNS_RESOLVER_H
#define DNS_RESOLVER_H
#include <netdb.h>
#include <stdlib.h>
#include <module.h>
#define DNS_RESOLVER_MODULE_NAME "network/dns_resolver/v1"
struct dns_resolver_module {
module_info module;
status_t (*getaddrinfo)(const char* node, const char* service,
const struct addrinfo* hints, struct addrinfo** res);
};
static inline int
kgetaddrinfo(const char* node, const char* service,
const struct addrinfo* hints, struct addrinfo** res)
{
dns_resolver_module* dns;
status_t result = get_module(DNS_RESOLVER_MODULE_NAME,
reinterpret_cast<module_info**>(&dns));
if (result != B_OK)
return result;
result = dns->getaddrinfo(node, service, hints, res);
put_module(DNS_RESOLVER_MODULE_NAME);
return result;
}
static inline void
kfreeaddrinfo(struct addrinfo* res)
{
free(res);
}
#define getaddrinfo kgetaddrinfo
#define freeaddrinfo kfreeaddrinfo
#endif // DNS_RESOLVER_H
+1
View File
@@ -11,6 +11,7 @@ SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems iso9660 ;
SubInclude HAIKU_TOP src add-ons kernel file_systems netfs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 ;
SubInclude HAIKU_TOP src add-ons kernel file_systems ntfs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems packagefs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems ramfs ;
@@ -0,0 +1,798 @@
/*
* Copyright 2012-2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "Connection.h"
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <AutoDeleter.h>
#include <util/kernel_cpp.h>
#include <net/dns_resolver.h>
#define NFS4_PORT 2049
#define LAST_FRAGMENT 0x80000000
#define MAX_PACKET_SIZE 65535
#define NFS_MIN_PORT 665
bool
PeerAddress::operator==(const PeerAddress& address)
{
return memcmp(&fAddress, &address.fAddress, sizeof(fAddress)) == 0
&& fProtocol == address.fProtocol;
}
bool
PeerAddress::operator<(const PeerAddress& address)
{
int compare = memcmp(&fAddress, &address.fAddress, sizeof(fAddress));
return compare < 0 || (compare == 0 && fProtocol < address.fProtocol);
}
PeerAddress&
PeerAddress::operator=(const PeerAddress& address)
{
fAddress = address.fAddress;
fProtocol = address.fProtocol;
return *this;
}
PeerAddress::PeerAddress()
:
fProtocol(0)
{
memset(&fAddress, 0, sizeof(fAddress));
}
PeerAddress::PeerAddress(int networkFamily)
:
fProtocol(0)
{
ASSERT(networkFamily == AF_INET || networkFamily == AF_INET6);
memset(&fAddress, 0, sizeof(fAddress));
fAddress.ss_family = networkFamily;
switch (networkFamily) {
case AF_INET:
fAddress.ss_len = sizeof(sockaddr_in);
break;
case AF_INET6:
fAddress.ss_len = sizeof(sockaddr_in6);
break;
}
}
const char*
PeerAddress::ProtocolString() const
{
static const char* tcpName = "tcp";
static const char* udpName = "udp";
static const char* unknown = "";
switch (fProtocol) {
case IPPROTO_TCP:
return tcpName;
case IPPROTO_UDP:
return udpName;
default:
return unknown;
}
}
void
PeerAddress::SetProtocol(const char* protocol)
{
ASSERT(protocol != NULL);
if (strcmp(protocol, "tcp") == 0)
fProtocol = IPPROTO_TCP;
else if (strcmp(protocol, "udp") == 0)
fProtocol = IPPROTO_UDP;
}
char*
PeerAddress::UniversalAddress() const
{
char* uAddr = reinterpret_cast<char*>(malloc(INET6_ADDRSTRLEN + 16));
if (uAddr == NULL)
return NULL;
if (inet_ntop(fAddress.ss_family, InAddr(), uAddr, AddressSize()) == NULL)
return NULL;
char port[16];
sprintf(port, ".%d.%d", Port() >> 8, Port() & 0xff);
strcat(uAddr, port);
return uAddr;
}
socklen_t
PeerAddress::AddressSize() const
{
switch (Family()) {
case AF_INET:
return sizeof(sockaddr_in);
case AF_INET6:
return sizeof(sockaddr_in6);
default:
return 0;
}
}
uint16
PeerAddress::Port() const
{
uint16 port;
switch (Family()) {
case AF_INET:
port = reinterpret_cast<const sockaddr_in*>(&fAddress)->sin_port;
break;
case AF_INET6:
port = reinterpret_cast<const sockaddr_in6*>(&fAddress)->sin6_port;
break;
default:
port = 0;
}
return ntohs(port);
}
void
PeerAddress::SetPort(uint16 port)
{
port = htons(port);
switch (Family()) {
case AF_INET:
reinterpret_cast<sockaddr_in*>(&fAddress)->sin_port = port;
break;
case AF_INET6:
reinterpret_cast<sockaddr_in6*>(&fAddress)->sin6_port = port;
break;
}
}
const void*
PeerAddress::InAddr() const
{
switch (Family()) {
case AF_INET:
return &reinterpret_cast<const sockaddr_in*>(&fAddress)->sin_addr;
case AF_INET6:
return &reinterpret_cast<const sockaddr_in6*>(&fAddress)->sin6_addr;
default:
return NULL;
}
}
size_t
PeerAddress::InAddrSize() const
{
switch (Family()) {
case AF_INET:
return sizeof(in_addr);
case AF_INET6:
return sizeof(in6_addr);
default:
return 0;
}
}
AddressResolver::AddressResolver(const char* name)
:
fHead(NULL),
fCurrent(NULL),
fForcedPort(htons(NFS4_PORT)),
fForcedProtocol(IPPROTO_TCP)
{
fStatus = ResolveAddress(name);
}
AddressResolver::~AddressResolver()
{
freeaddrinfo(fHead);
}
status_t
AddressResolver::ResolveAddress(const char* name)
{
ASSERT(name != NULL);
if (fHead != NULL) {
freeaddrinfo(fHead);
fHead = NULL;
fCurrent = NULL;
}
// getaddrinfo() is very expensive when called from kernel, so we do not
// want to call it unless there is no other choice.
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
if (inet_aton(name, &addr.sin_addr) == 1) {
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(NFS4_PORT);
memcpy(&fAddress.fAddress, &addr, sizeof(addr));
fAddress.fProtocol = IPPROTO_TCP;
return B_OK;
}
status_t result = getaddrinfo(name, NULL, NULL, &fHead);
fCurrent = fHead;
return result;
}
void
AddressResolver::ForceProtocol(const char* protocol)
{
ASSERT(protocol != NULL);
if (strcmp(protocol, "tcp") == 0)
fForcedProtocol = IPPROTO_TCP;
else if (strcmp(protocol, "udp") == 0)
fForcedProtocol = IPPROTO_UDP;
fAddress.SetProtocol(protocol);
}
void
AddressResolver::ForcePort(uint16 port)
{
fForcedPort = htons(port);
fAddress.SetPort(port);
}
status_t
AddressResolver::GetNextAddress(PeerAddress* address)
{
ASSERT(address != NULL);
if (fStatus != B_OK)
return fStatus;
if (fHead == NULL) {
*address = fAddress;
fStatus = B_NAME_NOT_FOUND;
return B_OK;
}
address->fProtocol = fForcedProtocol;
while (fCurrent != NULL) {
if (fCurrent->ai_family == AF_INET) {
memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in));
reinterpret_cast<sockaddr_in*>(&address->fAddress)->sin_port
= fForcedPort;
} else if (fCurrent->ai_family == AF_INET6) {
memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in6));
reinterpret_cast<sockaddr_in6*>(&address->fAddress)->sin6_port
= fForcedPort;
} else {
fCurrent = fCurrent->ai_next;
continue;
}
fCurrent = fCurrent->ai_next;
return B_OK;
}
return B_NAME_NOT_FOUND;
}
Connection::Connection(const PeerAddress& address)
:
ConnectionBase(address)
{
}
ConnectionListener::ConnectionListener(const PeerAddress& address)
:
ConnectionBase(address)
{
}
ConnectionBase::ConnectionBase(const PeerAddress& address)
:
fWaitCancel(create_sem(0, NULL)),
fSocket(-1),
fPeerAddress(address)
{
mutex_init(&fSocketLock, NULL);
}
ConnectionStream::ConnectionStream(const PeerAddress& address)
:
Connection(address)
{
}
ConnectionPacket::ConnectionPacket(const PeerAddress& address)
:
Connection(address)
{
}
ConnectionBase::~ConnectionBase()
{
if (fSocket != -1)
close(fSocket);
mutex_destroy(&fSocketLock);
delete_sem(fWaitCancel);
}
status_t
ConnectionBase::GetLocalAddress(PeerAddress* address)
{
ASSERT(address != NULL);
address->fProtocol = fPeerAddress.fProtocol;
socklen_t addressSize = sizeof(address->fAddress);
return getsockname(fSocket, (struct sockaddr*)&address->fAddress,
&addressSize);
}
status_t
ConnectionStream::Send(const void* buffer, uint32 size)
{
ASSERT(buffer != NULL);
status_t result;
uint32* buf = reinterpret_cast<uint32*>(malloc(size + sizeof(uint32)));
if (buf == NULL)
return B_NO_MEMORY;
MemoryDeleter _(buf);
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(&fSocketLock);
do {
result = send(fSocket, buf + sent, size + sizeof(uint32) - sent, 0);
sent += result;
} while (result > 0 && sent < size + sizeof(uint32));
mutex_unlock(&fSocketLock);
if (result < 0) {
result = errno;
return result;
} else if (result == 0)
return B_IO_ERROR;
return B_OK;
}
status_t
ConnectionPacket::Send(const void* buffer, uint32 size)
{
ASSERT(buffer != NULL);
ASSERT(size < 65535);
// send on DGRAM sockets is atomic. No need to lock.
status_t result = send(fSocket, buffer, size, 0);
if (result < 0)
return errno;
return B_OK;
}
status_t
ConnectionStream::Receive(void** _buffer, uint32* _size)
{
ASSERT(_buffer != NULL);
ASSERT(_size != NULL);
status_t result;
uint32 size = 0;
void* buffer = NULL;
uint32 record_size;
bool last_one = false;
object_wait_info object[2];
object[0].object = fWaitCancel;
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
object[1].object = fSocket;
object[1].type = B_OBJECT_TYPE_FD;
object[1].events = B_EVENT_READ;
do {
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
object[1].events = B_EVENT_READ;
result = wait_for_objects(object, 2);
if (result < B_OK
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
free(buffer);
return ECONNABORTED;
} else if ((object[1].events & B_EVENT_READ) == 0)
continue;
// There is only one listener thread per connection. No need to lock.
uint32 received = 0;
do {
result = recv(fSocket, ((uint8*)&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);
ASSERT(record_size > 0);
last_one = static_cast<int32>(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;
MemoryDeleter bufferDeleter(buffer);
received = 0;
do {
result = recv(fSocket, (uint8*)buffer + size + received,
record_size - received, 0);
received += result;
} while (result > 0 && received < record_size);
if (result < 0)
return errno;
else if (result == 0)
return ECONNABORTED;
bufferDeleter.Detach();
size += record_size;
} while (!last_one);
*_buffer = buffer;
*_size = size;
return B_OK;
}
status_t
ConnectionPacket::Receive(void** _buffer, uint32* _size)
{
ASSERT(_buffer != NULL);
ASSERT(_size != NULL);
status_t result;
int32 size = MAX_PACKET_SIZE;
void* buffer = malloc(size);
if (buffer == NULL)
return B_NO_MEMORY;
object_wait_info object[2];
object[0].object = fWaitCancel;
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
object[1].object = fSocket;
object[1].type = B_OBJECT_TYPE_FD;
object[1].events = B_EVENT_READ;
do {
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
object[1].events = B_EVENT_READ;
result = wait_for_objects(object, 2);
if (result < B_OK
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
free(buffer);
return ECONNABORTED;
} else if ((object[1].events & B_EVENT_READ) == 0)
continue;
break;
} while (true);
// There is only one listener thread per connection. No need to lock.
size = recv(fSocket, buffer, size, 0);
if (size < 0) {
result = errno;
free(buffer);
return result;
} else if (size == 0) {
free(buffer);
return ECONNABORTED;
}
*_buffer = buffer;
*_size = size;
return B_OK;
}
Connection*
Connection::CreateObject(const PeerAddress& address)
{
switch (address.fProtocol) {
case IPPROTO_TCP:
return new(std::nothrow) ConnectionStream(address);
case IPPROTO_UDP:
return new(std::nothrow) ConnectionPacket(address);
default:
return NULL;
}
}
status_t
Connection::Connect(Connection **_connection, const PeerAddress& address)
{
ASSERT(_connection != NULL);
Connection* conn = CreateObject(address);
if (conn == NULL)
return B_NO_MEMORY;
status_t result;
if (conn->fWaitCancel < B_OK) {
result = conn->fWaitCancel;
delete conn;
return result;
}
result = conn->Connect();
if (result != B_OK) {
delete conn;
return result;
}
*_connection = conn;
return B_OK;
}
status_t
Connection::SetTo(Connection **_connection, int socket,
const PeerAddress& address)
{
ASSERT(_connection != NULL);
ASSERT(socket != -1);
Connection* conn = CreateObject(address);
if (conn == NULL)
return B_NO_MEMORY;
status_t result;
if (conn->fWaitCancel < B_OK) {
result = conn->fWaitCancel;
delete conn;
return result;
}
conn->fSocket = socket;
*_connection = conn;
return B_OK;
}
status_t
Connection::Connect()
{
switch (fPeerAddress.fProtocol) {
case IPPROTO_TCP:
fSocket = socket(fPeerAddress.Family(), SOCK_STREAM, IPPROTO_TCP);
break;
case IPPROTO_UDP:
fSocket = socket(fPeerAddress.Family(), SOCK_DGRAM, IPPROTO_UDP);
break;
default:
return B_BAD_VALUE;
}
if (fSocket < 0)
return errno;
status_t result;
uint16 port, attempt = 0;
PeerAddress address(fPeerAddress.Family());
do {
port = rand() % (IPPORT_RESERVED - NFS_MIN_PORT);
port += NFS_MIN_PORT;
if (attempt == 9)
port = 0;
attempt++;
address.SetPort(port);
result = bind(fSocket, (sockaddr*)&address.fAddress,
address.AddressSize());
} while (attempt <= 10 && result != B_OK);
if (attempt > 10) {
close(fSocket);
return result;
}
result = connect(fSocket, (sockaddr*)&fPeerAddress.fAddress,
fPeerAddress.AddressSize());
if (result != 0) {
result = errno;
close(fSocket);
return result;
}
return B_OK;
}
status_t
Connection::Reconnect()
{
release_sem(fWaitCancel);
close(fSocket);
acquire_sem(fWaitCancel);
return Connect();
}
void
ConnectionBase::Disconnect()
{
release_sem(fWaitCancel);
close(fSocket);
fSocket = -1;
}
status_t
ConnectionListener::Listen(ConnectionListener** listener, int networkFamily,
uint16 port)
{
ASSERT(listener != NULL);
ASSERT(networkFamily == AF_INET || networkFamily == AF_INET6);
int sock = socket(networkFamily, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0)
return errno;
PeerAddress address(networkFamily);
address.SetPort(port);
address.fProtocol = IPPROTO_TCP;
status_t result = bind(sock, (sockaddr*)&address.fAddress,
address.AddressSize());
if (result != B_OK) {
close(sock);
return errno;
}
if (listen(sock, 5) != B_OK) {
close(sock);
return errno;
}
*listener = new(std::nothrow) ConnectionListener(address);
if (*listener == NULL) {
close(sock);
return B_NO_MEMORY;
}
if ((*listener)->fWaitCancel < B_OK) {
result = (*listener)->fWaitCancel;
close(sock);
delete *listener;
return result;
}
(*listener)->fSocket = sock;
return B_OK;
}
status_t
ConnectionListener::AcceptConnection(Connection** connection)
{
ASSERT(connection != NULL);
object_wait_info object[2];
object[0].object = fWaitCancel;
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
object[1].object = fSocket;
object[1].type = B_OBJECT_TYPE_FD;
object[1].events = B_EVENT_READ;
do {
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
object[1].events = B_EVENT_READ;
status_t result = wait_for_objects(object, 2);
if (result < B_OK
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
return ECONNABORTED;
} else if ((object[1].events & B_EVENT_READ) == 0)
continue;
break;
} while (true);
sockaddr_storage addr;
socklen_t length = sizeof(addr);
int sock = accept(fSocket, reinterpret_cast<sockaddr*>(&addr), &length);
if (sock < 0)
return errno;
PeerAddress address;
address.fProtocol = IPPROTO_TCP;
address.fAddress = addr;
status_t result = Connection::SetTo(connection, sock, address);
if (result != B_OK) {
close(sock);
return result;
}
return B_OK;
}
@@ -0,0 +1,146 @@
/*
* Copyright 2012-2013 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>
struct PeerAddress {
sockaddr_storage fAddress;
int fProtocol;
bool operator==(const PeerAddress& address);
bool operator<(const PeerAddress& address);
PeerAddress& operator=(const PeerAddress& address);
PeerAddress();
PeerAddress(int networkFamily);
inline int Family() const;
const char* ProtocolString() const;
void SetProtocol(const char* protocol);
char* UniversalAddress() const;
socklen_t AddressSize() const;
void SetPort(uint16 port);
uint16 Port() const;
const void* InAddr() const;
size_t InAddrSize() const;
};
inline int
PeerAddress::Family() const
{
return fAddress.ss_family;
}
struct addrinfo;
class AddressResolver {
public:
AddressResolver(const char* name);
~AddressResolver();
status_t GetNextAddress(PeerAddress* address);
void ForceProtocol(const char* protocol);
void ForcePort(uint16 port);
protected:
status_t ResolveAddress(const char* name);
private:
addrinfo* fHead;
addrinfo* fCurrent;
PeerAddress fAddress;
uint16 fForcedPort;
int fForcedProtocol;
status_t fStatus;
};
class ConnectionBase {
public:
ConnectionBase(const PeerAddress& address);
virtual ~ConnectionBase();
status_t GetLocalAddress(PeerAddress* address);
void Disconnect();
protected:
sem_id fWaitCancel;
int fSocket;
mutex fSocketLock;
const PeerAddress fPeerAddress;
};
class Connection : public ConnectionBase {
public:
static status_t Connect(Connection **connection,
const PeerAddress& address);
static status_t SetTo(Connection **connection, int socket,
const PeerAddress& address);
virtual status_t Send(const void* buffer, uint32 size) = 0;
virtual status_t Receive(void** buffer, uint32* size) = 0;
status_t Reconnect();
protected:
static Connection* CreateObject(const PeerAddress& address);
Connection(const PeerAddress& address);
status_t Connect();
};
class ConnectionStream : public Connection {
public:
ConnectionStream(const PeerAddress& address);
virtual status_t Send(const void* buffer, uint32 size);
virtual status_t Receive(void** buffer, uint32* size);
};
class ConnectionPacket : public Connection {
public:
ConnectionPacket(const PeerAddress& address);
virtual status_t Send(const void* buffer, uint32 size);
virtual status_t Receive(void** buffer, uint32* size);
};
class ConnectionListener : public ConnectionBase {
public:
static status_t Listen(ConnectionListener** listener, int networkFamily,
uint16 port = 0);
status_t AcceptConnection(Connection** connection);
protected:
ConnectionListener(const PeerAddress& address);
};
#endif // CONNECTION_H
@@ -0,0 +1,180 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "Cookie.h"
#include "Inode.h"
#include "Request.h"
LockOwner::LockOwner(uint32 owner)
:
fSequence(0),
fOwner(owner),
fUseCount(0),
fNext(NULL),
fPrev(NULL)
{
memset(fStateId, 0, sizeof(fStateId));
mutex_init(&fLock, NULL);
}
LockOwner::~LockOwner()
{
mutex_destroy(&fLock);
}
LockInfo::LockInfo(LockOwner* owner)
:
fOwner(owner)
{
ASSERT(owner != NULL);
fOwner->fUseCount++;
}
LockInfo::~LockInfo()
{
fOwner->fUseCount--;
}
bool
LockInfo::operator==(const struct flock& lock) const
{
bool eof = lock.l_len + lock.l_start == OFF_MAX;
uint64 start = static_cast<uint64>(lock.l_start);
uint64 len = static_cast<uint64>(lock.l_len);
return fStart == start && (fLength == len
|| (eof && fLength == UINT64_MAX));
}
bool
LockInfo::operator==(const LockInfo& lock) const
{
return fOwner == lock.fOwner && fStart == lock.fStart
&& fLength == lock.fLength && fType == lock.fType;
}
Cookie::Cookie()
:
fRequests(NULL),
fSnoozeCancel(create_sem(1, NULL))
{
acquire_sem(fSnoozeCancel);
mutex_init(&fRequestLock, NULL);
}
Cookie::~Cookie()
{
delete_sem(fSnoozeCancel);
mutex_destroy(&fRequestLock);
}
status_t
Cookie::RegisterRequest(RPC::Request* req)
{
ASSERT(req != NULL);
RequestEntry* ent = new RequestEntry;
if (ent == NULL)
return B_NO_MEMORY;
MutexLocker _(fRequestLock);
ent->fRequest = req;
ent->fNext = fRequests;
fRequests = ent;
return B_OK;
}
status_t
Cookie::UnregisterRequest(RPC::Request* req)
{
ASSERT(req != NULL);
MutexLocker _(fRequestLock);
RequestEntry* ent = fRequests;
RequestEntry* prev = NULL;
while (ent != NULL) {
if (ent->fRequest == req) {
if (prev == NULL)
fRequests = ent->fNext;
else
prev->fNext = ent->fNext;
delete ent;
}
prev = ent;
ent = ent->fNext;
}
return B_OK;
}
status_t
Cookie::CancelAll()
{
release_sem(fSnoozeCancel);
MutexLocker _(fRequestLock);
RequestEntry* ent = fRequests;
while (ent != NULL) {
fFileSystem->Server()->WakeCall(ent->fRequest);
ent = ent->fNext;
}
return B_OK;
}
OpenFileCookie::OpenFileCookie()
:
fLocks(NULL)
{
}
void
OpenFileCookie::AddLock(LockInfo* lock)
{
ASSERT(lock != NULL);
lock->fCookieNext = fLocks;
fLocks = lock;
}
void
OpenFileCookie::RemoveLock(LockInfo* lock, LockInfo* prev)
{
if (prev != NULL)
prev->fCookieNext = lock->fCookieNext;
else {
ASSERT(prev == NULL && fLocks == lock);
fLocks = lock->fCookieNext;
}
}
OpenDirCookie::~OpenDirCookie()
{
if (fSnapshot != NULL)
fSnapshot->ReleaseReference();
}
@@ -0,0 +1,106 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef COOKIE_H
#define COOKIE_H
#include <SupportDefs.h>
#include "DirectoryCache.h"
#include "FileSystem.h"
struct OpenState;
struct LockOwner {
uint64 fClientId;
uint32 fStateId[3];
uint32 fStateSeq;
uint32 fSequence;
uint32 fOwner;
uint32 fUseCount;
mutex fLock;
LockOwner* fNext;
LockOwner* fPrev;
LockOwner(uint32 owner);
~LockOwner();
};
struct LockInfo {
LockOwner* fOwner;
uint64 fStart;
uint64 fLength;
LockType fType;
LockInfo* fNext;
LockInfo* fCookieNext;
LockInfo(LockOwner* owner);
~LockInfo();
bool operator==(const struct flock& lock) const;
bool operator==(const LockInfo& lock) const;
};
struct Cookie {
struct RequestEntry {
RPC::Request* fRequest;
RequestEntry* fNext;
};
FileSystem* fFileSystem;
RequestEntry* fRequests;
mutex fRequestLock;
sem_id fSnoozeCancel;
Cookie();
virtual ~Cookie();
status_t RegisterRequest(RPC::Request* req);
status_t UnregisterRequest(RPC::Request* req);
status_t CancelAll();
};
struct OpenStateCookie : public Cookie {
OpenState* fOpenState;
uint32 fMode;
};
struct OpenFileCookie : public OpenStateCookie {
LockInfo* fLocks;
void AddLock(LockInfo* lock);
void RemoveLock(LockInfo* lock, LockInfo* prev);
OpenFileCookie();
};
struct OpenDirCookie : public Cookie {
int fSpecial;
DirectoryCacheSnapshot* fSnapshot;
NameCacheEntry* fCurrent;
bool fEOF;
bool fAttrDir;
~OpenDirCookie();
};
struct OpenAttrCookie : public OpenStateCookie { };
#endif // COOKIE_H
@@ -0,0 +1,65 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "Delegation.h"
#include "Inode.h"
#include "Request.h"
Delegation::Delegation(const OpenDelegationData& data, Inode* inode,
uint64 clientID, bool attribute)
:
fClientID(clientID),
fData(data),
fInode(inode),
fAttribute(attribute)
{
ASSERT(inode != NULL);
}
status_t
Delegation::GiveUp(bool truncate)
{
if (!fAttribute && !truncate)
fInode->SyncAndCommit(true);
ReturnDelegation();
return B_OK;
}
status_t
Delegation::ReturnDelegation()
{
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.DelegReturn(fData.fStateID, fData.fStateSeq);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(reply.NFS4Error(), serv, NULL, fInode->GetOpenState()))
continue;
reply.PutFH();
return reply.DelegReturn();
} while (true);
}
@@ -0,0 +1,65 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef DELEGATION_H
#define DELEGATION_H
#include <lock.h>
#include <SupportDefs.h>
#include "NFS4Object.h"
class Inode;
class Delegation : public NFS4Object,
public DoublyLinkedListLinkImpl<Delegation> {
public:
Delegation(const OpenDelegationData& data, Inode* inode,
uint64 clientID, bool attr = false);
status_t GiveUp(bool truncate = false);
inline void SetData(const OpenDelegationData& data);
inline Inode* GetInode();
inline OpenDelegation Type();
protected:
status_t ReturnDelegation();
private:
uint64 fClientID;
OpenDelegationData fData;
Inode* fInode;
bool fAttribute;
};
inline void
Delegation::SetData(const OpenDelegationData& data)
{
fData = data;
}
inline Inode*
Delegation::GetInode()
{
return fInode;
}
inline OpenDelegation
Delegation::Type()
{
return fData.fType;
}
#endif // DELEGATION_H
@@ -0,0 +1,345 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "DirectoryCache.h"
#include <fs_cache.h>
#include <NodeMonitor.h>
#include "Inode.h"
NameCacheEntry::NameCacheEntry(const char* name, ino_t node)
:
fNode(node),
fName(strdup(name))
{
ASSERT(name != NULL);
}
NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry)
:
fNode(entry.fNode),
fName(strdup(entry.fName))
{
}
NameCacheEntry::~NameCacheEntry()
{
free(const_cast<char*>(fName));
}
DirectoryCacheSnapshot::DirectoryCacheSnapshot()
{
mutex_init(&fLock, NULL);
}
DirectoryCacheSnapshot::DirectoryCacheSnapshot(
const DirectoryCacheSnapshot& snapshot)
{
mutex_init(&fLock, NULL);
MutexLocker _(snapshot.fLock);
NameCacheEntry* entry = snapshot.fEntries.Head();
NameCacheEntry* new_entry;
while (entry) {
new_entry = new NameCacheEntry(*entry);
if (new_entry == NULL)
break;
fEntries.Add(new_entry);
entry = snapshot.fEntries.GetNext(entry);
}
}
DirectoryCacheSnapshot::~DirectoryCacheSnapshot()
{
while (!fEntries.IsEmpty()) {
NameCacheEntry* current = fEntries.RemoveHead();
delete current;
}
mutex_destroy(&fLock);
}
DirectoryCache::DirectoryCache(Inode* inode, bool attr)
:
fRevalidated(false),
fDirectoryCache(NULL),
fInode(inode),
fAttrDir(attr),
fTrashed(true)
{
ASSERT(inode != NULL);
mutex_init(&fLock, NULL);
}
DirectoryCache::~DirectoryCache()
{
mutex_destroy(&fLock);
}
void
DirectoryCache::Reset()
{
Trash();
fExpireTime = system_time() + kExpirationTime;
fTrashed = false;
}
void
DirectoryCache::Trash()
{
while (!fNameCache.IsEmpty()) {
NameCacheEntry* current = fNameCache.RemoveHead();
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
current->fName);
delete current;
}
_SetSnapshot(NULL);
fTrashed = true;
}
status_t
DirectoryCache::AddEntry(const char* name, ino_t node, bool created)
{
ASSERT(name != NULL);
NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
if (entry == NULL)
return B_NO_MEMORY;
if (entry->fName == NULL) {
delete entry;
return B_NO_MEMORY;
}
fNameCache.Add(entry);
if (created && fDirectoryCache != NULL) {
MutexLocker _(fDirectoryCache->fLock);
NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
if (entry == NULL)
return B_NO_MEMORY;
if (entry->fName == NULL) {
delete entry;
return B_NO_MEMORY;
}
fDirectoryCache->fEntries.Add(entry);
}
if (!fAttrDir) {
return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(),
name, node);
}
return B_OK;
}
void
DirectoryCache::RemoveEntry(const char* name)
{
ASSERT(name != NULL);
SinglyLinkedList<NameCacheEntry>::Iterator iterator
= fNameCache.GetIterator();
NameCacheEntry* previous = NULL;
NameCacheEntry* current = iterator.Next();
while (current != NULL) {
if (strcmp(current->fName, name) == 0) {
fNameCache.Remove(previous, current);
delete current;
break;
}
previous = current;
current = iterator.Next();
}
if (fDirectoryCache != NULL) {
MutexLocker _(fDirectoryCache->fLock);
iterator = fDirectoryCache->fEntries.GetIterator();
previous = NULL;
current = iterator.Next();
while (current != NULL) {
if (strcmp(current->fName, name) == 0) {
fDirectoryCache->fEntries.Remove(previous, current);
delete current;
break;
}
previous = current;
current = iterator.Next();
}
}
if (!fAttrDir) {
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
name);
}
}
void
DirectoryCache::_SetSnapshot(DirectoryCacheSnapshot* snapshot)
{
if (fDirectoryCache != NULL)
fDirectoryCache->ReleaseReference();
fDirectoryCache = snapshot;
}
status_t
DirectoryCache::_LoadSnapshot(bool trash)
{
DirectoryCacheSnapshot* oldSnapshot = fDirectoryCache;
if (oldSnapshot != NULL)
oldSnapshot->AcquireReference();
if (trash)
Trash();
DirectoryCacheSnapshot* newSnapshot;
status_t result = fInode->GetDirSnapshot(&newSnapshot, NULL, &fChange,
fAttrDir);
if (result != B_OK) {
if (oldSnapshot != NULL)
oldSnapshot->ReleaseReference();
return result;
}
newSnapshot->AcquireReference();
_SetSnapshot(newSnapshot);
fExpireTime = system_time() + kExpirationTime;
fTrashed = false;
if (oldSnapshot != NULL)
NotifyChanges(oldSnapshot, newSnapshot);
if (oldSnapshot != NULL)
oldSnapshot->ReleaseReference();
newSnapshot->ReleaseReference();
return B_OK;
}
status_t
DirectoryCache::Revalidate()
{
if (fExpireTime < system_time())
return B_OK;
uint64 change;
if (fInode->GetChangeInfo(&change, fAttrDir) != B_OK) {
Trash();
return B_ERROR;
}
if (change == fChange) {
fExpireTime = system_time() + kExpirationTime;
return B_OK;
}
return _LoadSnapshot(true);
}
void
DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
DirectoryCacheSnapshot* newSnapshot)
{
ASSERT(newSnapshot != NULL);
ASSERT(oldSnapshot != NULL);
MutexLocker _(newSnapshot->fLock);
SinglyLinkedList<NameCacheEntry>::Iterator oldIt
= oldSnapshot->fEntries.GetIterator();
NameCacheEntry* oldCurrent;
SinglyLinkedList<NameCacheEntry>::Iterator newIt
= newSnapshot->fEntries.GetIterator();
NameCacheEntry* newCurrent = newIt.Next();
while (newCurrent != NULL) {
oldIt = oldSnapshot->fEntries.GetIterator();
oldCurrent = oldIt.Next();
bool found = false;
NameCacheEntry* prev = NULL;
while (oldCurrent != NULL) {
if (oldCurrent->fNode == newCurrent->fNode
&& strcmp(oldCurrent->fName, newCurrent->fName) == 0) {
found = true;
break;
}
prev = oldCurrent;
oldCurrent = oldIt.Next();
}
if (!found) {
if (fAttrDir) {
notify_attribute_changed(fInode->GetFileSystem()->DevId(),
fInode->ID(), newCurrent->fName, B_ATTR_CREATED);
} else {
notify_entry_created(fInode->GetFileSystem()->DevId(),
fInode->ID(), newCurrent->fName, newCurrent->fNode);
do {
FileInfo fi;
fi.fFileId = newCurrent->fNode;
fi.fParent = fInode->fInfo.fHandle;
status_t result = fi.CreateName(fInode->fInfo.fPath,
newCurrent->fName);
if (result != B_OK)
break;
fInode->GetFileSystem()->InoIdMap()->AddEntry(fi,
Inode::FileIdToInoT(newCurrent->fNode), true);
} while (false);
}
} else
oldSnapshot->fEntries.Remove(prev, oldCurrent);
newCurrent = newIt.Next();
}
oldIt = oldSnapshot->fEntries.GetIterator();
oldCurrent = oldIt.Next();
while (oldCurrent != NULL) {
if (fAttrDir) {
notify_attribute_changed(fInode->GetFileSystem()->DevId(),
fInode->ID(), newCurrent->fName, B_ATTR_REMOVED);
} else {
notify_entry_removed(fInode->GetFileSystem()->DevId(), fInode->ID(),
oldCurrent->fName, oldCurrent->fNode);
}
oldCurrent = oldIt.Next();
}
}
@@ -0,0 +1,175 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef DIRECTORYCACHE_H
#define DIRECTORYCACHE_H
#include <lock.h>
#include <SupportDefs.h>
#include <util/DoublyLinkedList.h>
#include <util/KernelReferenceable.h>
#include <util/SinglyLinkedList.h>
class Inode;
struct NameCacheEntry :
public SinglyLinkedListLinkImpl<NameCacheEntry> {
ino_t fNode;
const char* fName;
NameCacheEntry(const char* name, ino_t node);
NameCacheEntry(const NameCacheEntry& entry);
~NameCacheEntry();
};
struct DirectoryCacheSnapshot : public KernelReferenceable {
SinglyLinkedList<NameCacheEntry> fEntries;
mutable mutex fLock;
DirectoryCacheSnapshot();
DirectoryCacheSnapshot(
const DirectoryCacheSnapshot& snapshot);
~DirectoryCacheSnapshot();
};
class DirectoryCache {
public:
DirectoryCache(Inode* inode, bool attr = false);
~DirectoryCache();
inline void Lock();
inline void Unlock();
void Reset();
void Trash();
inline bool Valid();
status_t AddEntry(const char* name, ino_t node,
bool created = false);
void RemoveEntry(const char* name);
inline status_t GetSnapshot(DirectoryCacheSnapshot** snapshot);
inline SinglyLinkedList<NameCacheEntry>& EntriesList();
status_t Revalidate();
inline status_t ValidateChangeInfo(uint64 change);
inline void SetChangeInfo(uint64 change);
inline uint64 ChangeInfo();
inline Inode* GetInode();
static const bigtime_t kExpirationTime = 15000000;
bool fRevalidated;
protected:
void NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
DirectoryCacheSnapshot* newSnapshot);
private:
void _SetSnapshot(DirectoryCacheSnapshot* snapshot);
status_t _LoadSnapshot(bool trash);
SinglyLinkedList<NameCacheEntry> fNameCache;
DirectoryCacheSnapshot* fDirectoryCache;
Inode* fInode;
bool fAttrDir;
bool fTrashed;
mutex fLock;
uint64 fChange;
bigtime_t fExpireTime;
};
inline void
DirectoryCache::Lock()
{
mutex_lock(&fLock);
}
inline void
DirectoryCache::Unlock()
{
mutex_unlock(&fLock);
}
inline bool
DirectoryCache::Valid()
{
return !fTrashed;
}
inline status_t
DirectoryCache::GetSnapshot(DirectoryCacheSnapshot** snapshot)
{
ASSERT(snapshot != NULL);
status_t result = B_OK;
if (fDirectoryCache == NULL)
result = _LoadSnapshot(false);
*snapshot = fDirectoryCache;
return result;
}
inline SinglyLinkedList<NameCacheEntry>&
DirectoryCache::EntriesList()
{
return fNameCache;
}
inline status_t
DirectoryCache::ValidateChangeInfo(uint64 change)
{
if (fTrashed || change != fChange) {
Trash();
fChange = change;
fExpireTime = system_time() + kExpirationTime;
fTrashed = false;
return B_ERROR;
}
return B_OK;
}
inline void
DirectoryCache::SetChangeInfo(uint64 change)
{
fExpireTime = system_time() + kExpirationTime;
fChange = change;
}
inline uint64
DirectoryCache::ChangeInfo()
{
return fChange;
}
inline Inode*
DirectoryCache::GetInode()
{
return fInode;
}
#endif // DIRECTORYCACHE_H
@@ -0,0 +1,144 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "FileInfo.h"
#include "FileSystem.h"
#include "Request.h"
status_t
FileInfo::ParsePath(RequestBuilder& req, uint32& count, const char* _path)
{
ASSERT(_path != NULL);
char* path = strdup(_path);
if (path == NULL)
return B_NO_MEMORY;
char* pathStart = path;
char* pathEnd;
while (pathStart != NULL) {
pathEnd = strchr(pathStart, '/');
if (pathEnd != NULL)
*pathEnd = '\0';
if (pathEnd != pathStart) {
if (!strcmp(pathStart, "..")) {
req.LookUpUp();
count++;
} else if (strcmp(pathStart, ".")) {
req.LookUp(pathStart);
count++;
}
}
if (pathEnd != NULL && pathEnd[1] != '\0')
pathStart = pathEnd + 1;
else
pathStart = NULL;
}
free(path);
return B_OK;
}
status_t
FileInfo::CreateName(const char* dirPath, const char* name)
{
ASSERT(name != NULL);
free(const_cast<char*>(fName));
fName = strdup(name);
if (fName == NULL)
return B_NO_MEMORY;
free(const_cast<char*>(fPath));
fPath = NULL;
if (dirPath != NULL) {
char* path = reinterpret_cast<char*>(malloc(strlen(name) + 2
+ strlen(dirPath)));
if (path == NULL)
return B_NO_MEMORY;
strcpy(path, dirPath);
strcat(path, "/");
strcat(path, name);
fPath = path;
} else
fPath = strdup(name);
if (fPath == NULL)
return B_NO_MEMORY;
return B_OK;
}
status_t
FileInfo::UpdateFileHandles(FileSystem* fs)
{
ASSERT(fs != NULL);
Request request(fs->Server(), fs);
RequestBuilder& req = request.Builder();
req.PutRootFH();
uint32 lookupCount = 0;
status_t result;
result = ParsePath(req, lookupCount, fs->Path());
if (result != B_OK)
return result;
result = ParsePath(req, lookupCount, fPath);
if (result != B_OK)
return result;
if (fs->IsAttrSupported(FATTR4_FILEID)) {
AttrValue attr;
attr.fAttribute = FATTR4_FILEID;
attr.fFreePointer = false;
attr.fData.fValue64 = fFileId;
req.Verify(&attr, 1);
}
req.GetFH();
req.LookUpUp();
req.GetFH();
result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
reply.PutRootFH();
for (uint32 i = 0; i < lookupCount; i++)
reply.LookUp();
if (fs->IsAttrSupported(FATTR4_FILEID)) {
result = reply.Verify();
if (result != B_OK)
return result;
}
reply.GetFH(&fHandle);
if (reply.LookUpUp() == B_ENTRY_NOT_FOUND) {
fParent = fHandle;
return B_OK;
}
return reply.GetFH(&fParent);
}
@@ -0,0 +1,188 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef FILEINFO_H
#define FILEINFO_H
#include <stdlib.h>
#include <string.h>
#include <SupportDefs.h>
#define NFS4_FHSIZE 128
struct FileHandle {
uint8 fSize;
uint8 fData[NFS4_FHSIZE];
inline FileHandle();
inline FileHandle(const FileHandle& fh);
inline FileHandle& operator=(const FileHandle& fh);
inline bool operator!=(const FileHandle& handle) const;
inline bool operator>(const FileHandle& handle) const;
inline bool operator<(const FileHandle& handle) const;
};
class FileSystem;
class RequestBuilder;
// Complete information needed to identify a file in any situation.
// Unfortunately just a FileHandle is not enough even when they are persistent
// since OPEN requires both parent FileHandle and file name (just like LOOKUP).
struct FileInfo {
uint64 fFileId;
FileHandle fHandle;
FileHandle fParent;
const char* fName;
const char* fPath;
FileHandle fAttrDir;
inline FileInfo();
inline ~FileInfo();
inline FileInfo(const FileInfo& fi);
inline FileInfo& operator=(const FileInfo& fi);
status_t UpdateFileHandles(FileSystem* fs);
static status_t ParsePath(RequestBuilder& req, uint32& count,
const char* _path);
status_t CreateName(const char* dirPath, const char* name);
};
struct FileSystemId {
uint64 fMajor;
uint64 fMinor;
inline bool operator==(const FileSystemId& fsid) const;
inline bool operator!=(const FileSystemId& fsid) const;
};
inline
FileHandle::FileHandle()
:
fSize(0)
{
}
inline
FileHandle::FileHandle(const FileHandle& fh)
:
fSize(fh.fSize)
{
memcpy(fData, fh.fData, fSize);
}
inline FileHandle&
FileHandle::operator=(const FileHandle& fh)
{
fSize = fh.fSize;
memcpy(fData, fh.fData, fSize);
return *this;
}
inline bool
FileHandle::operator!=(const FileHandle& handle) const
{
if (fSize != handle.fSize)
return true;
return memcmp(fData, handle.fData, fSize) != 0;
}
inline bool
FileHandle::operator>(const FileHandle& handle) const
{
if (fSize > handle.fSize)
return true;
return memcmp(fData, handle.fData, fSize) > 0;
}
inline bool
FileHandle::operator<(const FileHandle& handle) const
{
if (fSize < handle.fSize)
return true;
return memcmp(fData, handle.fData, fSize) < 0;
}
inline
FileInfo::FileInfo()
:
fFileId(0),
fName(NULL),
fPath(NULL)
{
}
inline
FileInfo::~FileInfo()
{
free(const_cast<char*>(fName));
free(const_cast<char*>(fPath));
}
inline
FileInfo::FileInfo(const FileInfo& fi)
:
fFileId(fi.fFileId),
fHandle(fi.fHandle),
fParent(fi.fParent),
fName(strdup(fi.fName)),
fPath(strdup(fi.fPath))
{
}
inline FileInfo&
FileInfo::operator=(const FileInfo& fi)
{
fFileId = fi.fFileId;
fHandle = fi.fHandle;
fParent = fi.fParent;
free(const_cast<char*>(fName));
fName = strdup(fi.fName);
free(const_cast<char*>(fPath));
fPath = strdup(fi.fPath);
return *this;
}
inline bool
FileSystemId::operator==(const FileSystemId& fsid) const
{
return fMajor == fsid.fMajor && fMinor == fsid.fMinor;
}
inline bool
FileSystemId::operator!=(const FileSystemId& fsid) const
{
return !operator==(fsid);
}
#endif // FILEHINFO_H
@@ -0,0 +1,393 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "FileSystem.h"
#include <string.h>
#include <AutoDeleter.h>
#include <lock.h>
#include "Request.h"
#include "RootInode.h"
extern RPC::ServerManager* gRPCServerManager;
extern RPC::ProgramData* CreateNFS4Server(RPC::Server* serv);
FileSystem::FileSystem(const MountConfiguration& configuration)
:
fOpenCount(0),
fOpenOwnerSequence(0),
fNamedAttrs(true),
fPath(NULL),
fRoot(NULL),
fId(1),
fConfiguration(configuration)
{
fOpenOwner = rand();
fOpenOwner <<= 32;
fOpenOwner |= rand();
mutex_init(&fOpenOwnerLock, NULL);
mutex_init(&fOpenLock, NULL);
mutex_init(&fDelegationLock, NULL);
mutex_init(&fCreateFileLock, NULL);
}
FileSystem::~FileSystem()
{
NFS4Server* server = reinterpret_cast<NFS4Server*>(fServer->PrivateData());
if (server != NULL)
server->RemoveFileSystem(this);
mutex_destroy(&fDelegationLock);
mutex_destroy(&fOpenLock);
mutex_destroy(&fOpenOwnerLock);
mutex_destroy(&fCreateFileLock);
free(const_cast<char*>(fPath));
delete fRoot;
}
static const char*
GetPath(const char* root, const char* path)
{
ASSERT(path != NULL);
int slash = 0;
int i;
for (i = 0; path[i] != '\0'; i++) {
if (path[i] == '/')
slash = i;
if (root == NULL)
break;
if (path[i] != root[i] || root[i] == '\0')
break;
}
if (path[i] == '\0')
return NULL;
return path + slash;
}
status_t
FileSystem::Mount(FileSystem** _fs, RPC::Server* serv, const char* fsPath,
dev_t id, const MountConfiguration& configuration)
{
ASSERT(_fs != NULL);
ASSERT(serv != NULL);
ASSERT(fsPath != NULL);
FileSystem* fs = new(std::nothrow) FileSystem(configuration);
if (fs == NULL)
return B_NO_MEMORY;
ObjectDeleter<FileSystem> fsDeleter(fs);
Request request(serv, fs);
RequestBuilder& req = request.Builder();
req.PutRootFH();
uint32 lookupCount = 0;
status_t result = FileInfo::ParsePath(req, lookupCount, fsPath);
if (result != B_OK)
return result;
req.GetFH();
req.Access();
Attribute attr[] = { FATTR4_SUPPORTED_ATTRS, FATTR4_FH_EXPIRE_TYPE,
FATTR4_FSID, FATTR4_FS_LOCATIONS };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
reply.PutRootFH();
for (uint32 i = 0; i < lookupCount; i++)
reply.LookUp();
FileHandle fh;
reply.GetFH(&fh);
uint32 allowed;
result = reply.Access(NULL, &allowed);
if (result != B_OK)
return result;
else if ((allowed & (ACCESS4_READ | ACCESS4_LOOKUP))
!= (ACCESS4_READ | ACCESS4_LOOKUP))
return B_PERMISSION_DENIED;
AttrValue* values;
uint32 count;
result = reply.GetAttr(&values, &count);
if (result != B_OK || count < 2)
return result;
// FATTR4_SUPPORTED_ATTRS is mandatory
memcpy(fs->fSupAttrs, &values[0].fData.fValue64, sizeof(fs->fSupAttrs));
// FATTR4_FH_EXPIRE_TYPE is mandatory
fs->fExpireType = values[1].fData.fValue32;
// FATTR4_FSID is mandatory
FileSystemId* fsid
= reinterpret_cast<FileSystemId*>(values[2].fData.fPointer);
if (count == 4 && values[3].fAttribute == FATTR4_FS_LOCATIONS) {
FSLocations* locs
= reinterpret_cast<FSLocations*>(values[3].fData.fLocations);
fs->fPath = strdup(locs->fRootPath);
} else
fs->fPath = NULL;
FileInfo fi;
const char* name;
if (fsPath != NULL && fsPath[0] == '/')
fsPath++;
fs->fServer = serv;
fs->fDevId = id;
fs->fFsId = *fsid;
fi.fHandle = fh;
fi.fParent = fh;
fi.fPath = strdup(GetPath(fs->fPath, fsPath));
if (fi.fPath != NULL) {
name = strrchr(fi.fPath, '/');
if (name != NULL) {
name++;
fi.fName = strdup(name);
}
}
delete[] values;
Inode* inode;
result = Inode::CreateInode(fs, fi, &inode);
if (result != B_OK)
return result;
name = strrchr(fsPath, '/');
if (name != NULL) {
name++;
reinterpret_cast<RootInode*>(inode)->SetName(name);
} else if (fsPath[0] != '\0')
reinterpret_cast<RootInode*>(inode)->SetName(fsPath);
else {
char* address = serv->ID().UniversalAddress();
if (address != NULL)
reinterpret_cast<RootInode*>(inode)->SetName(address);
else
reinterpret_cast<RootInode*>(inode)->SetName("NFS4 Share");
free(address);
}
fs->fRoot = reinterpret_cast<RootInode*>(inode);
fs->NFSServer()->AddFileSystem(fs);
*_fs = fs;
fsDeleter.Detach();
return B_OK;
}
status_t
FileSystem::GetInode(ino_t id, Inode** _inode)
{
ASSERT(_inode != NULL);
FileInfo fi;
status_t result = fInoIdMap.GetFileInfo(&fi, id);
ASSERT(result != B_ENTRY_NOT_FOUND);
if (result != B_OK)
return result;
Inode* inode;
result = Inode::CreateInode(this, fi, &inode);
if (result != B_OK)
return result;
*_inode = inode;
return B_OK;
}
status_t
FileSystem::Migrate(const RPC::Server* serv)
{
ASSERT(serv != NULL);
MutexLocker _(fOpenLock);
if (serv != fServer)
return B_OK;
if (!fRoot->ProbeMigration())
return B_OK;
AttrValue* values;
status_t result = fRoot->GetLocations(&values);
if (result != B_OK)
return result;
FSLocations* locs
= reinterpret_cast<FSLocations*>(values[0].fData.fLocations);
RPC::Server* server = fServer;
for (uint32 i = 0; i < locs->fCount; i++) {
for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) {
AddressResolver resolver(locs->fLocations[i].fLocations[j]);
if (gRPCServerManager->Acquire(&fServer, &resolver,
CreateNFS4Server) == B_OK) {
free(const_cast<char*>(fPath));
fPath = strdup(locs->fLocations[i].fRootPath);
if (fPath == NULL) {
gRPCServerManager->Release(fServer);
fServer = server;
delete[] values;
return B_NO_MEMORY;
}
break;
}
}
}
delete[] values;
if (server == fServer) {
gRPCServerManager->Release(server);
return B_ERROR;
}
NFS4Server* old = reinterpret_cast<NFS4Server*>(server->PrivateData());
old->RemoveFileSystem(this);
NFSServer()->AddFileSystem(this);
gRPCServerManager->Release(server);
return B_OK;
}
DoublyLinkedList<OpenState>&
FileSystem::OpenFilesLock()
{
mutex_lock(&fOpenLock);
return fOpenFiles;
}
void
FileSystem::OpenFilesUnlock()
{
mutex_unlock(&fOpenLock);
}
void
FileSystem::AddOpenFile(OpenState* state)
{
ASSERT(state != NULL);
MutexLocker _(fOpenLock);
fOpenFiles.InsertBefore(fOpenFiles.Head(), state);
NFSServer()->IncUsage();
}
void
FileSystem::RemoveOpenFile(OpenState* state)
{
ASSERT(state != NULL);
MutexLocker _(fOpenLock);
fOpenFiles.Remove(state);
NFSServer()->DecUsage();
}
DoublyLinkedList<Delegation>&
FileSystem::DelegationsLock()
{
mutex_lock(&fDelegationLock);
return fDelegationList;
}
void
FileSystem::DelegationsUnlock()
{
mutex_unlock(&fDelegationLock);
}
void
FileSystem::AddDelegation(Delegation* delegation)
{
ASSERT(delegation != NULL);
MutexLocker _(fDelegationLock);
fDelegationList.InsertBefore(fDelegationList.Head(), delegation);
fHandleToDelegation.Remove(delegation->fInfo.fHandle);
fHandleToDelegation.Insert(delegation->fInfo.fHandle, delegation);
}
void
FileSystem::RemoveDelegation(Delegation* delegation)
{
ASSERT(delegation != NULL);
MutexLocker _(fDelegationLock);
fDelegationList.Remove(delegation);
fHandleToDelegation.Remove(delegation->fInfo.fHandle);
}
Delegation*
FileSystem::GetDelegation(const FileHandle& handle)
{
MutexLocker _(fDelegationLock);
AVLTreeMap<FileHandle, Delegation*>::Iterator it;
it = fHandleToDelegation.Find(handle);
if (!it.HasCurrent())
return NULL;
return it.Current();
}
@@ -0,0 +1,247 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include "Delegation.h"
#include "InodeIdMap.h"
#include "NFS4Defs.h"
#include "NFS4Server.h"
class Inode;
class RootInode;
struct MountConfiguration {
bool fHard;
int fRetryLimit;
bigtime_t fRequestTimeout;
bool fEmulateNamedAttrs;
bool fCacheMetadata;
};
class FileSystem : public DoublyLinkedListLinkImpl<FileSystem> {
public:
static status_t Mount(FileSystem** pfs, RPC::Server* serv,
const char* path, dev_t id,
const MountConfiguration& configuration);
~FileSystem();
status_t GetInode(ino_t id, Inode** inode);
inline RootInode* Root();
status_t Migrate(const RPC::Server* serv);
DoublyLinkedList<OpenState>& OpenFilesLock();
void OpenFilesUnlock();
inline uint32 OpenFilesCount();
void AddOpenFile(OpenState* state);
void RemoveOpenFile(OpenState* state);
DoublyLinkedList<Delegation>& DelegationsLock();
void DelegationsUnlock();
void AddDelegation(Delegation* delegation);
void RemoveDelegation(Delegation* delegation);
Delegation* GetDelegation(const FileHandle& handle);
inline bool IsAttrSupported(Attribute attr) const;
inline uint32 ExpireType() const;
inline RPC::Server* Server();
inline NFS4Server* NFSServer();
inline const char* Path() const;
inline const FileSystemId& FsId() const;
inline uint64 AllocFileId();
inline dev_t DevId() const;
inline InodeIdMap* InoIdMap();
inline uint64 OpenOwner() const;
inline uint32 OpenOwnerSequenceLock();
inline void OpenOwnerSequenceUnlock(uint32 sequence);
inline bool NamedAttrs();
inline void SetNamedAttrs(bool attrs);
inline const MountConfiguration& GetConfiguration();
inline mutex& CreateFileLock();
private:
FileSystem(const MountConfiguration& config);
mutex fCreateFileLock;
mutex fDelegationLock;
DoublyLinkedList<Delegation> fDelegationList;
AVLTreeMap<FileHandle, Delegation*> fHandleToDelegation;
DoublyLinkedList<OpenState> fOpenFiles;
uint32 fOpenCount;
mutex fOpenLock;
uint64 fOpenOwner;
uint32 fOpenOwnerSequence;
mutex fOpenOwnerLock;
uint32 fExpireType;
uint32 fSupAttrs[2];
bool fNamedAttrs;
FileSystemId fFsId;
const char* fPath;
RootInode* fRoot;
RPC::Server* fServer;
vint64 fId;
dev_t fDevId;
InodeIdMap fInoIdMap;
MountConfiguration fConfiguration;
};
inline RootInode*
FileSystem::Root()
{
return fRoot;
}
inline uint32
FileSystem::OpenFilesCount()
{
return fOpenCount;
}
inline bool
FileSystem::IsAttrSupported(Attribute attr) const
{
return sIsAttrSet(attr, fSupAttrs, 2);
}
inline uint32
FileSystem::ExpireType() const
{
return fExpireType;
}
inline RPC::Server*
FileSystem::Server()
{
ASSERT(fServer != NULL);
return fServer;
}
inline NFS4Server*
FileSystem::NFSServer()
{
ASSERT(fServer->PrivateData() != NULL);
return reinterpret_cast<NFS4Server*>(fServer->PrivateData());
}
inline const char*
FileSystem::Path() const
{
ASSERT(fPath != NULL);
return fPath;
}
inline const FileSystemId&
FileSystem::FsId() const
{
return fFsId;
}
inline uint64
FileSystem::AllocFileId()
{
return atomic_add64(&fId, 1);
}
inline dev_t
FileSystem::DevId() const
{
return fDevId;
}
inline InodeIdMap*
FileSystem::InoIdMap()
{
return &fInoIdMap;
}
inline uint64
FileSystem::OpenOwner() const
{
return fOpenOwner;
}
inline uint32
FileSystem::OpenOwnerSequenceLock()
{
mutex_lock(&fOpenOwnerLock);
return fOpenOwnerSequence;
}
inline void
FileSystem::OpenOwnerSequenceUnlock(uint32 sequence)
{
fOpenOwnerSequence = sequence;
mutex_unlock(&fOpenOwnerLock);
}
inline bool
FileSystem::NamedAttrs()
{
return fNamedAttrs;
}
inline void
FileSystem::SetNamedAttrs(bool attrs)
{
fNamedAttrs = attrs;
}
inline const MountConfiguration&
FileSystem::GetConfiguration()
{
return fConfiguration;
}
inline mutex&
FileSystem::CreateFileLock()
{
return fCreateFileLock;
}
#endif // FILESYSTEM_H
@@ -0,0 +1,190 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "IdMap.h"
#include <AutoDeleter.h>
#include <FindDirectory.h>
#include <team.h>
#include <util/AutoLock.h>
#include "idmapper/IdMapper.h"
IdMap* gIdMapper = NULL;
mutex gIdMapperLock;
IdMap::IdMap()
{
mutex_init(&fLock, NULL);
fInitStatus = _Repair();
}
IdMap::~IdMap()
{
delete_port(fRequestPort);
delete_port(fReplyPort);
mutex_destroy(&fLock);
}
uid_t
IdMap::GetUserId(const char* owner)
{
ASSERT(owner != NULL);
return _GetValue<uid_t>(owner, MsgNameToUID);
}
gid_t
IdMap::GetGroupId(const char* ownerGroup)
{
ASSERT(ownerGroup != NULL);
return _GetValue<gid_t>(ownerGroup, MsgNameToGID);
}
char*
IdMap::GetOwner(uid_t user)
{
return reinterpret_cast<char*>(_GetBuffer(user, MsgUIDToName));
}
char*
IdMap::GetOwnerGroup(gid_t group)
{
return reinterpret_cast<char*>(_GetBuffer(group, MsgGIDToName));
}
template<typename T>
T
IdMap::_GetValue(const char* buffer, int32 code)
{
ASSERT(buffer != NULL);
MutexLocker _(fLock);
do {
status_t result = write_port(fRequestPort, MsgNameToUID, buffer,
strlen(buffer) + 1);
if (result != B_OK) {
if (_Repair() != B_OK)
return 0;
continue;
}
int32 code;
T value;
result = read_port(fReplyPort, &code, &value, sizeof(T));
if (result < B_OK) {
if (_Repair() != B_OK)
return 0;
continue;
}
if (code != MsgReply)
return 0;
return value;
} while (true);
}
template<typename T>
void*
IdMap::_GetBuffer(T value, int32 code)
{
MutexLocker _(fLock);
do {
status_t result = write_port(fRequestPort, code, &value, sizeof(value));
if (result != B_OK) {
if (_Repair() != B_OK)
return NULL;
continue;
}
ssize_t size = port_buffer_size(fReplyPort);
if (size < B_OK) {
if (_Repair() != B_OK)
return NULL;
continue;
}
int32 code;
void* buffer = malloc(size);
if (buffer == NULL)
return NULL;
MemoryDeleter bufferDeleter(buffer);
size = read_port(fReplyPort, &code, buffer, size);
if (size < B_OK) {
if (_Repair() != B_OK)
return 0;
continue;
}
if (code != MsgReply)
return NULL;
bufferDeleter.Detach();
return buffer;
} while (true);
}
status_t
IdMap::_Repair()
{
status_t result = B_OK;
fRequestPort = create_port(1, kRequestPortName);
if (fRequestPort < B_OK)
return fRequestPort;
fReplyPort = create_port(1, kReplyPortName);
if (fReplyPort < B_OK) {
delete_port(fRequestPort);
return fReplyPort;
}
char path[256];
if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast<dev_t>(-1),
false, path, sizeof(path)) != B_OK) {
delete_port(fReplyPort);
delete_port(fRequestPort);
return B_NAME_NOT_FOUND;
}
strlcat(path, "/nfs4_idmapper_server", sizeof(path));
const char* args[] = { path, NULL };
thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY,
B_SYSTEM_TEAM, 0);
if (thread < B_OK) {
delete_port(fReplyPort);
delete_port(fRequestPort);
return thread;
}
set_port_owner(fRequestPort, thread);
set_port_owner(fReplyPort, thread);
result = resume_thread(thread);
if (result != B_OK) {
kill_thread(thread);
delete_port(fReplyPort);
delete_port(fRequestPort);
return result;
}
return B_OK;
}
@@ -0,0 +1,60 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef IDMAP_H
#define IDMAP_H
#include <lock.h>
#include <port.h>
#include <SupportDefs.h>
class IdMap {
public:
IdMap();
~IdMap();
uid_t GetUserId(const char* owner);
gid_t GetGroupId(const char* ownerGroup);
char* GetOwner(uid_t user);
char* GetOwnerGroup(gid_t group);
inline status_t InitStatus();
private:
status_t _Repair();
template<typename T>
void* _GetBuffer(T value, int32 code);
template<typename T>
T _GetValue(const char* buffer, int32 code);
status_t fInitStatus;
mutex fLock;
port_id fRequestPort;
port_id fReplyPort;
};
inline status_t
IdMap::InitStatus()
{
return fInitStatus;
}
extern IdMap* gIdMapper;
extern mutex gIdMapperLock;
#endif // IDMAP_H
@@ -0,0 +1,981 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "Inode.h"
#include <ctype.h>
#include <string.h>
#include <AutoDeleter.h>
#include <fs_cache.h>
#include <NodeMonitor.h>
#include "IdMap.h"
#include "Request.h"
#include "RootInode.h"
Inode::Inode()
:
fMetaCache(this),
fCache(NULL),
fAttrCache(NULL),
fDelegation(NULL),
fFileCache(NULL),
fMaxFileSize(0),
fOpenState(NULL),
fWriteDirty(false),
fAIOWait(create_sem(1, NULL)),
fAIOCount(0)
{
rw_lock_init(&fDelegationLock, NULL);
mutex_init(&fStateLock, NULL);
mutex_init(&fFileCacheLock, NULL);
rw_lock_init(&fWriteLock, NULL);
mutex_init(&fAIOLock, NULL);
}
status_t
Inode::CreateInode(FileSystem* fs, const FileInfo& fi, Inode** _inode)
{
ASSERT(fs != NULL);
ASSERT(_inode != NULL);
Inode* inode = NULL;
if (fs->Root() == NULL)
inode = new(std::nothrow) RootInode;
else
inode = new(std::nothrow) Inode;
if (inode == NULL)
return B_NO_MEMORY;
inode->fInfo = fi;
inode->fFileSystem = fs;
uint64 size;
do {
RPC::Server* serv = fs->Server();
Request request(serv, fs);
RequestBuilder& req = request.Builder();
req.PutFH(inode->fInfo.fHandle);
Attribute attr[] = { FATTR4_TYPE, FATTR4_CHANGE, FATTR4_SIZE,
FATTR4_FSID, FATTR4_FILEID };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (inode->HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
AttrValue* values;
uint32 count;
result = reply.GetAttr(&values, &count);
if (result != B_OK || count < 4)
return result;
if (fi.fFileId == 0) {
if (count < 5 || values[4].fAttribute != FATTR4_FILEID)
inode->fInfo.fFileId = fs->AllocFileId();
else
inode->fInfo.fFileId = values[4].fData.fValue64;
} else
inode->fInfo.fFileId = fi.fFileId;
// FATTR4_TYPE is mandatory
inode->fType = values[0].fData.fValue32;
if (inode->fType == NF4DIR)
inode->fCache = new DirectoryCache(inode);
inode->fAttrCache = new DirectoryCache(inode, true);
// FATTR4_CHANGE is mandatory
inode->fChange = values[1].fData.fValue64;
// FATTR4_SIZE is mandatory
size = values[2].fData.fValue64;
// FATTR4_FSID is mandatory
FileSystemId* fsid
= reinterpret_cast<FileSystemId*>(values[3].fData.fPointer);
if (*fsid != fs->FsId()) {
delete[] values;
return B_ENTRY_NOT_FOUND;
}
delete[] values;
*_inode = inode;
break;
} while (true);
if (inode->fType == NF4REG)
inode->fFileCache = file_cache_create(fs->DevId(), inode->ID(), size);
return B_OK;
}
Inode::~Inode()
{
if (fDelegation != NULL)
RecallDelegation();
if (fFileCache != NULL)
file_cache_delete(fFileCache);
delete fCache;
delete fAttrCache;
delete_sem(fAIOWait);
mutex_destroy(&fAIOLock);
mutex_destroy(&fStateLock);
mutex_destroy(&fFileCacheLock);
rw_lock_destroy(&fDelegationLock);
rw_lock_destroy(&fWriteLock);
ASSERT(fAIOCount == 0);
}
status_t
Inode::RevalidateFileCache()
{
if (fDelegation != NULL)
return B_OK;
uint64 change;
status_t result = GetChangeInfo(&change);
if (result != B_OK)
return result;
MutexLocker _(fFileCacheLock);
if (change == fChange)
return B_OK;
struct stat st;
result = Stat(&st);
if (result != B_OK)
return result;
SyncAndCommit(true);
file_cache_delete(fFileCache);
fFileCache = file_cache_create(fFileSystem->DevId(), ID(), st.st_size);
change = fChange;
return B_OK;
}
status_t
Inode::LookUp(const char* name, ino_t* id)
{
ASSERT(name != NULL);
ASSERT(id != NULL);
if (fType != NF4DIR)
return B_NOT_A_DIRECTORY;
uint64 change;
uint64 fileID;
FileHandle handle;
status_t result = NFS4Inode::LookUp(name, &change, &fileID, &handle);
if (result != B_OK)
return result;
*id = FileIdToInoT(fileID);
result = ChildAdded(name, fileID, handle);
if (result != B_OK)
return result;
fCache->Lock();
if (!fCache->Valid()) {
fCache->Reset();
fCache->SetChangeInfo(change);
} else
fCache->ValidateChangeInfo(change);
fCache->AddEntry(name, *id);
fCache->Unlock();
return B_OK;
}
status_t
Inode::Link(Inode* dir, const char* name)
{
ASSERT(dir != NULL);
ASSERT(name != NULL);
ChangeInfo changeInfo;
status_t result = NFS4Inode::Link(dir, name, &changeInfo);
if (result != B_OK)
return result;
fFileSystem->Root()->MakeInfoInvalid();
FileInfo fi = fInfo;
fi.fParent = dir->fInfo.fHandle;
result = fi.CreateName(fInfo.fPath, name);
if (result != B_OK)
return result;
fFileSystem->InoIdMap()->AddEntry(fi, fInfo.fFileId);
dir->fCache->Lock();
if (dir->fCache->Valid()) {
if (changeInfo.fAtomic
&& dir->fCache->ChangeInfo() == changeInfo.fBefore) {
dir->fCache->AddEntry(name, fInfo.fFileId, true);
dir->fCache->SetChangeInfo(changeInfo.fAfter);
} else
dir->fCache->Trash();
}
dir->fCache->Unlock();
notify_entry_created(fFileSystem->DevId(), dir->ID(), name, ID());
return B_OK;
}
status_t
Inode::Remove(const char* name, FileType type, ino_t* id)
{
ASSERT(name != NULL);
MemoryDeleter nameDeleter;
if (type == NF4NAMEDATTR) {
status_t result = LoadAttrDirHandle();
if (result != B_OK)
return result;
name = AttrToFileName(name);
if (name == NULL)
return B_NO_MEMORY;
nameDeleter.SetTo(const_cast<char*>(name));
}
ChangeInfo changeInfo;
uint64 fileID;
status_t result = NFS4Inode::RemoveObject(name, type, &changeInfo, &fileID);
if (result != B_OK)
return result;
DirectoryCache* cache = type != NF4NAMEDATTR ? fCache : fAttrCache;
cache->Lock();
if (cache->Valid()) {
if (changeInfo.fAtomic
&& fCache->ChangeInfo() == changeInfo.fBefore) {
cache->RemoveEntry(name);
cache->SetChangeInfo(changeInfo.fAfter);
} else if (cache->ChangeInfo() != changeInfo.fBefore)
cache->Trash();
}
cache->Unlock();
fFileSystem->Root()->MakeInfoInvalid();
if (id != NULL)
*id = FileIdToInoT(fileID);
if (type == NF4NAMEDATTR) {
notify_attribute_changed(fFileSystem->DevId(), ID(), name,
B_ATTR_REMOVED);
} else {
notify_entry_removed(fFileSystem->DevId(), ID(), name,
FileIdToInoT(fileID));
}
return B_OK;
}
status_t
Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName,
bool attribute, ino_t* id)
{
ASSERT(from != NULL);
ASSERT(fromName != NULL);
ASSERT(to != NULL);
ASSERT(toName != NULL);
if (from->fFileSystem != to->fFileSystem)
return B_DONT_DO_THAT;
MemoryDeleter fromNameDeleter;
MemoryDeleter toNameDeleter;
if (attribute) {
status_t result = from->LoadAttrDirHandle();
if (result != B_OK)
return result;
result = to->LoadAttrDirHandle();
if (result != B_OK)
return result;
fromName = from->AttrToFileName(fromName);
toName = to->AttrToFileName(toName);
fromNameDeleter.SetTo(const_cast<char*>(fromName));
toNameDeleter.SetTo(const_cast<char*>(toName));
if (fromName == NULL || toName == NULL)
return B_NO_MEMORY;
}
ChangeInfo fromChange, toChange;
uint64 fileID;
status_t result = NFS4Inode::RenameNode(from, to, fromName, toName,
&fromChange, &toChange, &fileID, attribute);
if (result != B_OK)
return result;
from->fFileSystem->Root()->MakeInfoInvalid();
DirectoryCache* cache = attribute ? from->fAttrCache : from->fCache;
cache->Lock();
if (cache->Valid()) {
if (fromChange.fAtomic
&& cache->ChangeInfo() == fromChange.fBefore) {
cache->RemoveEntry(fromName);
cache->SetChangeInfo(fromChange.fAfter);
} else if (cache->ChangeInfo() != fromChange.fBefore)
cache->Trash();
}
cache->Unlock();
if (id != NULL)
*id = FileIdToInoT(fileID);
cache = attribute ? to->fAttrCache : to->fCache;
cache->Lock();
if (cache->Valid()) {
if (toChange.fAtomic
&& cache->ChangeInfo() == toChange.fBefore) {
cache->AddEntry(toName, fileID, true);
cache->SetChangeInfo(toChange.fAfter);
} else if (to->fCache->ChangeInfo() != toChange.fBefore)
cache->Trash();
}
cache->Unlock();
if (attribute) {
notify_attribute_changed(from->fFileSystem->DevId(), from->ID(),
fromName, B_ATTR_REMOVED);
notify_attribute_changed(to->fFileSystem->DevId(), to->ID(), toName,
B_ATTR_CREATED);
} else {
notify_entry_moved(from->fFileSystem->DevId(), from->ID(), fromName,
to->ID(), toName, FileIdToInoT(fileID));
}
return B_OK;
}
status_t
Inode::CreateLink(const char* name, const char* path, int mode, ino_t* id)
{
return CreateObject(name, path, mode, NF4LNK, id);
}
status_t
Inode::CreateObject(const char* name, const char* path, int mode, FileType type,
ino_t* id)
{
ASSERT(name != NULL);
ASSERT(type != NF4LNK || path != NULL);
ChangeInfo changeInfo;
uint64 fileID;
FileHandle handle;
status_t result = NFS4Inode::CreateObject(name, path, mode, type, &changeInfo,
&fileID, &handle);
if (result != B_OK)
return B_OK;
fFileSystem->Root()->MakeInfoInvalid();
result = ChildAdded(name, fileID, handle);
if (result != B_OK)
return B_OK;
fCache->Lock();
if (fCache->Valid()) {
if (changeInfo.fAtomic && fCache->ChangeInfo() == changeInfo.fBefore) {
fCache->AddEntry(name, fileID, true);
fCache->SetChangeInfo(changeInfo.fAfter);
} else
fCache->Trash();
}
fCache->Unlock();
notify_entry_created(fFileSystem->DevId(), ID(), name,
FileIdToInoT(fileID));
*id = FileIdToInoT(fileID);
return B_OK;
}
status_t
Inode::Access(int mode)
{
int acc = 0;
uint32 allowed;
bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
status_t result = fMetaCache.GetAccess(geteuid(), &allowed);
if (result != B_OK || !cache) {
result = NFS4Inode::Access(&allowed);
if (result != B_OK)
return result;
fMetaCache.SetAccess(geteuid(), allowed);
}
if ((allowed & ACCESS4_READ) != 0)
acc |= R_OK;
if ((allowed & ACCESS4_LOOKUP) != 0)
acc |= X_OK | R_OK;
if ((allowed & ACCESS4_EXECUTE) != 0)
acc |= X_OK;
if ((allowed & ACCESS4_MODIFY) != 0)
acc |= W_OK;
if ((mode & acc) != mode)
return B_NOT_ALLOWED;
return B_OK;
}
status_t
Inode::Stat(struct stat* st, OpenAttrCookie* attr)
{
ASSERT(st != NULL);
if (attr != NULL)
return GetStat(st, attr);
bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
if (!cache)
return GetStat(st, NULL);
status_t result = fMetaCache.GetStat(st);
if (result != B_OK) {
struct stat temp;
result = GetStat(&temp);
if (result != B_OK)
return result;
fMetaCache.SetStat(temp);
fMetaCache.GetStat(st);
}
return B_OK;
}
status_t
Inode::GetStat(struct stat* st, OpenAttrCookie* attr)
{
ASSERT(st != NULL);
AttrValue* values;
uint32 count;
status_t result = NFS4Inode::GetStat(&values, &count, attr);
if (result != B_OK)
return result;
// FATTR4_SIZE is mandatory
if (count < 1 || values[0].fAttribute != FATTR4_SIZE) {
delete[] values;
return B_BAD_VALUE;
}
st->st_size = values[0].fData.fValue64;
uint32 next = 1;
st->st_mode = Type();
if (count >= next && values[next].fAttribute == FATTR4_MODE) {
st->st_mode |= values[next].fData.fValue32;
next++;
} else
st->st_mode = 777;
if (count >= next && values[next].fAttribute == FATTR4_NUMLINKS) {
st->st_nlink = values[next].fData.fValue32;
next++;
} else
st->st_nlink = 1;
if (count >= next && values[next].fAttribute == FATTR4_OWNER) {
char* owner = reinterpret_cast<char*>(values[next].fData.fPointer);
if (owner != NULL && isdigit(owner[0]))
st->st_uid = atoi(owner);
else
st->st_uid = gIdMapper->GetUserId(owner);
next++;
} else
st->st_uid = 0;
if (count >= next && values[next].fAttribute == FATTR4_OWNER_GROUP) {
char* group = reinterpret_cast<char*>(values[next].fData.fPointer);
if (group != NULL && isdigit(group[0]))
st->st_gid = atoi(group);
else
st->st_gid = gIdMapper->GetGroupId(group);
next++;
} else
st->st_gid = 0;
if (count >= next && values[next].fAttribute == FATTR4_TIME_ACCESS) {
memcpy(&st->st_atim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_atim, 0, sizeof(timespec));
if (count >= next && values[next].fAttribute == FATTR4_TIME_CREATE) {
memcpy(&st->st_crtim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_crtim, 0, sizeof(timespec));
if (count >= next && values[next].fAttribute == FATTR4_TIME_METADATA) {
memcpy(&st->st_ctim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_ctim, 0, sizeof(timespec));
if (count >= next && values[next].fAttribute == FATTR4_TIME_MODIFY) {
memcpy(&st->st_mtim, values[next].fData.fPointer,
sizeof(timespec));
next++;
} else
memset(&st->st_mtim, 0, sizeof(timespec));
delete[] values;
st->st_blksize = fFileSystem->Root()->IOSize();
st->st_blocks = st->st_size / st->st_blksize;
st->st_blocks += st->st_size % st->st_blksize == 0 ? 0 : 1;
return B_OK;
}
status_t
Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie)
{
ASSERT(st != NULL);
status_t result;
AttrValue attr[6];
uint32 i = 0;
if ((mask & B_STAT_SIZE) != 0) {
attr[i].fAttribute = FATTR4_SIZE;
attr[i].fFreePointer = false;
attr[i].fData.fValue64 = st->st_size;
i++;
}
if ((mask & B_STAT_MODE) != 0) {
attr[i].fAttribute = FATTR4_MODE;
attr[i].fFreePointer = false;
attr[i].fData.fValue32 = st->st_mode;
i++;
}
if ((mask & B_STAT_UID) != 0) {
attr[i].fAttribute = FATTR4_OWNER;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = gIdMapper->GetOwner(st->st_uid);
i++;
}
if ((mask & B_STAT_GID) != 0) {
attr[i].fAttribute = FATTR4_OWNER_GROUP;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = gIdMapper->GetOwnerGroup(st->st_gid);
i++;
}
if ((mask & B_STAT_ACCESS_TIME) != 0) {
attr[i].fAttribute = FATTR4_TIME_ACCESS_SET;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = malloc(sizeof(st->st_atim));
memcpy(attr[i].fData.fPointer, &st->st_atim, sizeof(st->st_atim));
i++;
}
if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
attr[i].fAttribute = FATTR4_TIME_MODIFY_SET;
attr[i].fFreePointer = true;
attr[i].fData.fPointer = malloc(sizeof(st->st_mtim));
memcpy(attr[i].fData.fPointer, &st->st_mtim, sizeof(st->st_mtim));
i++;
}
if (cookie == NULL) {
MutexLocker stateLocker(fStateLock);
ASSERT(fOpenState != NULL);
result = NFS4Inode::WriteStat(fOpenState, attr, i);
stateLocker.Unlock();
fMetaCache.InvalidateStat();
if ((mask & B_STAT_MODE) != 0 || (mask & B_STAT_UID) != 0
|| (mask & B_STAT_GID) != 0) {
fMetaCache.InvalidateAccess();
}
} else
result = NFS4Inode::WriteStat(cookie->fOpenState, attr, i);
return result;
}
inline status_t
Inode::CheckLockType(short ltype, uint32 mode)
{
switch (ltype) {
case F_UNLCK:
return B_OK;
case F_RDLCK:
if ((mode & O_RDONLY) == 0 && (mode & O_RDWR) == 0)
return EBADF;
return B_OK;
case F_WRLCK:
if ((mode & O_WRONLY) == 0 && (mode & O_RDWR) == 0)
return EBADF;
return B_OK;
default:
return B_BAD_VALUE;
}
}
status_t
Inode::TestLock(OpenFileCookie* cookie, struct flock* lock)
{
ASSERT(cookie != NULL);
ASSERT(lock != NULL);
if (lock->l_type == F_UNLCK)
return B_OK;
status_t result = CheckLockType(lock->l_type, cookie->fMode);
if (result != B_OK)
return result;
LockType ltype = sGetLockType(lock->l_type, false);
uint64 position = lock->l_start;
uint64 length = lock->l_len;
bool conflict;
result = NFS4Inode::TestLock(cookie, &ltype, &position, &length, conflict);
if (result != B_OK)
return result;
if (conflict) {
lock->l_type = sLockTypeToHaiku(ltype);
lock->l_start = static_cast<off_t>(position);
if (length >= OFF_MAX)
lock->l_len = OFF_MAX;
else
lock->l_len = static_cast<off_t>(length);
} else
lock->l_type = F_UNLCK;
return B_OK;
}
status_t
Inode::AcquireLock(OpenFileCookie* cookie, const struct flock* lock,
bool wait)
{
ASSERT(cookie != NULL);
ASSERT(lock != NULL);
OpenState* state = cookie->fOpenState;
status_t result = CheckLockType(lock->l_type, cookie->fMode);
if (result != B_OK)
return result;
thread_info info;
get_thread_info(find_thread(NULL), &info);
MutexLocker locker(state->fOwnerLock);
LockOwner* owner = state->GetLockOwner(info.team);
if (owner == NULL)
return B_NO_MEMORY;
LockInfo* linfo = new LockInfo(owner);
if (linfo == NULL)
return B_NO_MEMORY;
locker.Unlock();
linfo->fStart = lock->l_start;
if (lock->l_len + lock->l_start == OFF_MAX)
linfo->fLength = UINT64_MAX;
else
linfo->fLength = lock->l_len;
linfo->fType = sGetLockType(lock->l_type, wait);
result = NFS4Inode::AcquireLock(cookie, linfo, wait);
if (result != B_OK)
return result;
MutexLocker _(state->fLocksLock);
state->AddLock(linfo);
cookie->AddLock(linfo);
return B_OK;
}
status_t
Inode::ReleaseLock(OpenFileCookie* cookie, const struct flock* lock)
{
ASSERT(cookie != NULL);
ASSERT(lock != NULL);
SyncAndCommit();
LockInfo* prev = NULL;
thread_info info;
get_thread_info(find_thread(NULL), &info);
uint32 owner = info.team;
OpenState* state = cookie->fOpenState;
MutexLocker locker(state->fLocksLock);
LockInfo* linfo = state->fLocks;
while (linfo != NULL) {
if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
state->RemoveLock(linfo, prev);
break;
}
prev = linfo;
linfo = linfo->fNext;
}
prev = NULL;
linfo = cookie->fLocks;
while (linfo != NULL) {
if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
cookie->RemoveLock(linfo, prev);
break;
}
prev = linfo;
linfo = linfo->fCookieNext;
}
locker.Unlock();
if (linfo == NULL)
return B_BAD_VALUE;
status_t result = NFS4Inode::ReleaseLock(cookie, linfo);
if (result != B_OK)
return result;
state->DeleteLock(linfo);
return B_OK;
}
status_t
Inode::ReleaseAllLocks(OpenFileCookie* cookie)
{
ASSERT(cookie != NULL);
SyncAndCommit();
OpenState* state = cookie->fOpenState;
MutexLocker _(state->fLocksLock);
LockInfo* linfo = cookie->fLocks;
while (linfo != NULL) {
cookie->RemoveLock(linfo, NULL);
LockInfo* prev = NULL;
LockInfo* stateLock = state->fLocks;
while (stateLock != NULL) {
if (*linfo == *stateLock) {
state->RemoveLock(stateLock, prev);
break;
}
prev = stateLock;
stateLock = stateLock->fNext;
}
NFS4Inode::ReleaseLock(cookie, linfo);
state->DeleteLock(linfo);
linfo = cookie->fLocks;
}
return B_OK;
}
status_t
Inode::ChildAdded(const char* name, uint64 fileID,
const FileHandle& fileHandle)
{
ASSERT(name != NULL);
fFileSystem->Root()->MakeInfoInvalid();
FileInfo fi;
fi.fFileId = fileID;
fi.fHandle = fileHandle;
fi.fParent = fInfo.fHandle;
status_t result = fi.CreateName(fInfo.fPath, name);
if (result != B_OK)
return result;
return fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID));
}
const char*
Inode::Name() const
{
return fInfo.fName;
}
void
Inode::SetDelegation(Delegation* delegation)
{
ASSERT(delegation != NULL);
WriteLocker _(fDelegationLock);
fMetaCache.InvalidateStat();
struct stat st;
Stat(&st);
fMetaCache.LockValid();
fDelegation = delegation;
fOpenState->AcquireReference();
fOpenState->fDelegation = delegation;
fFileSystem->AddDelegation(delegation);
}
void
Inode::RecallDelegation(bool truncate)
{
WriteLocker _(fDelegationLock);
if (fDelegation == NULL)
return;
ReturnDelegation(truncate);
}
void
Inode::RecallReadDelegation()
{
WriteLocker _(fDelegationLock);
if (fDelegation == NULL || fDelegation->Type() != OPEN_DELEGATE_READ)
return;
ReturnDelegation(false);
}
void
Inode::ReturnDelegation(bool truncate)
{
ASSERT(fDelegation != NULL);
fDelegation->GiveUp(truncate);
fMetaCache.UnlockValid();
fFileSystem->RemoveDelegation(fDelegation);
MutexLocker stateLocker(fStateLock);
fOpenState->fDelegation = NULL;
ReleaseOpenState();
delete fDelegation;
fDelegation = NULL;
}
void
Inode::ReleaseOpenState()
{
ASSERT(fOpenState != NULL);
if (fOpenState->ReleaseReference() == 1) {
ASSERT(fAIOCount == 0);
fOpenState = NULL;
}
}
status_t
Inode::SyncAndCommit(bool force)
{
if (!force && fDelegation != NULL)
return B_OK;
file_cache_sync(fFileCache);
WaitAIOComplete();
return Commit();
}
void
Inode::BeginAIOOp()
{
MutexLocker _(fAIOLock);
fAIOCount++;
if (fAIOCount == 1)
acquire_sem(fAIOWait);
}
void
Inode::EndAIOOp()
{
MutexLocker _(fAIOLock);
ASSERT(fAIOCount > 0);
fAIOCount--;
if (fAIOCount == 0)
release_sem(fAIOWait);
}
@@ -0,0 +1,258 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef INODE_H
#define INODE_H
#include "DirectoryCache.h"
#include "MetadataCache.h"
#include "NFS4Inode.h"
#include "OpenState.h"
class Delegation;
class Inode : public NFS4Inode {
public:
static status_t CreateInode(FileSystem* fs, const FileInfo& fi,
Inode** inode);
virtual ~Inode();
inline ino_t ID() const;
inline mode_t Type() const;
virtual const char* Name() const;
inline FileSystem* GetFileSystem() const;
inline void SetOpenState(OpenState* state);
inline void* FileCache();
status_t RevalidateFileCache();
inline uint64 MaxFileSize();
inline uint64 Change();
inline bool Dirty();
inline OpenState* GetOpenState();
void SetDelegation(Delegation* delegation);
void RecallDelegation(bool truncate = false);
void RecallReadDelegation();
status_t LookUp(const char* name, ino_t* id);
status_t Access(int mode);
status_t Commit();
status_t SyncAndCommit(bool force = false);
status_t CreateObject(const char* name, const char* path,
int mode, FileType type, ino_t* id);
status_t CreateLink(const char* name, const char* path,
int mode, ino_t* id);
status_t Link(Inode* dir, const char* name);
status_t Remove(const char* name, FileType type,
ino_t* id = NULL);
static status_t Rename(Inode* from, Inode* to,
const char* fromName, const char* toName,
bool attribute = false, ino_t* id = NULL);
status_t Stat(struct stat* st,
OpenAttrCookie* attr = NULL);
status_t WriteStat(const struct stat* st, uint32 mask,
OpenAttrCookie* attr = NULL);
status_t Create(const char* name, int mode, int perms,
OpenFileCookie* cookie,
OpenDelegationData* data, ino_t* id);
status_t Open(int mode, OpenFileCookie* cookie);
status_t Close(OpenFileCookie* cookie);
status_t OpenAttr(const char* name, int mode,
OpenAttrCookie* cookie, bool create,
int32 type = 0);
status_t CloseAttr(OpenAttrCookie* cookie);
status_t Read(OpenFileCookie* cookie, off_t pos,
void* buffer, size_t* length);
status_t Write(OpenFileCookie* cookie, off_t pos,
const void* buffer, size_t* _length);
status_t ReadDirect(OpenStateCookie* cookie, off_t pos,
void* buffer, size_t* length, bool* eof);
status_t WriteDirect(OpenStateCookie* cookie, off_t pos,
const void* buffer, size_t* _length);
status_t CreateDir(const char* name, int mode,
ino_t* id);
status_t OpenDir(OpenDirCookie* cookie);
status_t ReadDir(void* buffer, uint32 size,
uint32* count, OpenDirCookie* cookie);
status_t OpenAttrDir(OpenDirCookie* cookie);
status_t TestLock(OpenFileCookie* cookie,
struct flock* lock);
status_t AcquireLock(OpenFileCookie* cookie,
const struct flock* lock, bool wait);
status_t ReleaseLock(OpenFileCookie* cookie,
const struct flock* lock);
status_t ReleaseAllLocks(OpenFileCookie* cookie);
status_t GetDirSnapshot(DirectoryCacheSnapshot**
_snapshot, OpenDirCookie* cookie,
uint64* _change, bool attribute);
status_t LoadAttrDirHandle();
static inline ino_t FileIdToInoT(uint64 fileid);
void BeginAIOOp();
void EndAIOOp();
inline void WaitAIOComplete();
protected:
Inode();
void ReleaseOpenState();
status_t CreateState(const char* name, int mode,
int perms, OpenState* state,
OpenDelegationData* data);
void ReturnDelegation(bool truncate);
status_t ReadDirUp(struct dirent* de, uint32 pos,
uint32 size);
status_t FillDirEntry(struct dirent* de, ino_t id,
const char* name, uint32 pos, uint32 size);
status_t ChildAdded(const char* name, uint64 fileID,
const FileHandle& fileHandle);
status_t GetStat(struct stat* st,
OpenAttrCookie* attr = NULL);
char* AttrToFileName(const char* path);
static inline status_t CheckLockType(short ltype, uint32 mode);
private:
uint32 fType;
MetadataCache fMetaCache;
DirectoryCache* fCache;
DirectoryCache* fAttrCache;
rw_lock fDelegationLock;
Delegation* fDelegation;
uint64 fChange;
void* fFileCache;
mutex fFileCacheLock;
uint64 fMaxFileSize;
OpenState* fOpenState;
mutex fStateLock;
rw_lock fWriteLock;
bool fWriteDirty;
sem_id fAIOWait;
uint32 fAIOCount;
mutex fAIOLock;
};
inline void
Inode::WaitAIOComplete()
{
acquire_sem(fAIOWait);
release_sem(fAIOWait);
}
inline ino_t
Inode::FileIdToInoT(uint64 fileid)
{
if (sizeof(ino_t) >= sizeof(uint64))
return fileid;
else
return (ino_t)fileid ^ (fileid >>
(sizeof(uint64) - sizeof(ino_t)) * 8);
}
inline ino_t
Inode::ID() const
{
return FileIdToInoT(fInfo.fFileId);
}
inline mode_t
Inode::Type() const
{
return sNFSFileTypeToHaiku[fType];
}
inline FileSystem*
Inode::GetFileSystem() const
{
ASSERT(fFileSystem != NULL);
return fFileSystem;
}
inline void*
Inode::FileCache()
{
return fFileCache;
}
inline void
Inode::SetOpenState(OpenState* state)
{
ASSERT(state != NULL);
fOpenState = state;
}
inline uint64
Inode::MaxFileSize()
{
return fMaxFileSize;
}
inline uint64
Inode::Change()
{
return fChange;
}
inline bool
Inode::Dirty()
{
return fWriteDirty;
}
inline OpenState*
Inode::GetOpenState()
{
return fOpenState;
}
#endif // INODE_H
@@ -0,0 +1,416 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Inode.h"
#include <dirent.h>
#include <string.h>
#include "IdMap.h"
#include "Request.h"
#include "RootInode.h"
status_t
Inode::CreateDir(const char* name, int mode, ino_t* id)
{
return CreateObject(name, NULL, mode, NF4DIR, id);
}
status_t
Inode::OpenDir(OpenDirCookie* cookie)
{
ASSERT(cookie != NULL);
if (fType != NF4DIR)
return B_NOT_A_DIRECTORY;
status_t result = Access(R_OK);
if (result != B_OK)
return result;
cookie->fFileSystem = fFileSystem;
cookie->fSpecial = 0;
cookie->fSnapshot = NULL;
cookie->fCurrent = NULL;
cookie->fEOF = false;
cookie->fAttrDir = false;
return B_OK;
}
status_t
Inode::OpenAttrDir(OpenDirCookie* cookie)
{
ASSERT(cookie != NULL);
cookie->fFileSystem = fFileSystem;
cookie->fSpecial = 0;
cookie->fSnapshot = NULL;
cookie->fCurrent = NULL;
cookie->fEOF = false;
cookie->fAttrDir = true;
return LoadAttrDirHandle();
}
status_t
Inode::LoadAttrDirHandle()
{
if (fInfo.fAttrDir.fSize != 0)
return B_OK;
FileHandle handle;
status_t result;
if (fFileSystem->NamedAttrs()) {
result = NFS4Inode::OpenAttrDir(&handle);
if (result == B_OK) {
fInfo.fAttrDir = handle;
return B_OK;
}
if (result != B_UNSUPPORTED)
return result;
fFileSystem->SetNamedAttrs(false);
}
if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs)
return B_UNSUPPORTED;
char* attrDir
= reinterpret_cast<char*>(malloc(strlen(Name()) + 32));
if (attrDir == NULL)
return B_NO_MEMORY;
strcpy(attrDir, ".");
strcat(attrDir, Name());
strcat(attrDir, "-haiku-attrs");
result = NFS4Inode::LookUp(attrDir, NULL, NULL, &handle, true);
if (result == B_ENTRY_NOT_FOUND) {
ChangeInfo change;
struct stat st;
Stat(&st);
st.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
result = NFS4Inode::CreateObject(attrDir, NULL, st.st_mode, NF4DIR,
&change, NULL, &handle, true);
}
free(attrDir);
if (result != B_OK)
return result;
fInfo.fAttrDir = handle;
return B_OK;
}
status_t
Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
uint32 size)
{
ASSERT(de != NULL);
ASSERT(name != NULL);
uint32 nameSize = strlen(name) + 1;
const uint32 entSize = sizeof(struct dirent);
if (pos + entSize + nameSize > size)
return B_BUFFER_OVERFLOW;
de->d_dev = fFileSystem->DevId();
de->d_ino = id;
de->d_reclen = entSize + nameSize;
if (de->d_reclen % 8 != 0)
de->d_reclen += 8 - de->d_reclen % 8;
strcpy(de->d_name, name);
return B_OK;
}
status_t
Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
{
ASSERT(de != NULL);
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.LookUpUp();
req.GetFH();
if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
Attribute attr[] = { FATTR4_FILEID };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
}
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
result = reply.LookUpUp();
if (result != B_OK)
return result;
FileHandle fh;
reply.GetFH(&fh);
uint64 fileId;
if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
AttrValue* values;
uint32 count;
reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
fileId = values[0].fData.fValue64;
delete[] values;
} else
fileId = fFileSystem->AllocFileId();
return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size);
} while (true);
}
static char*
FileToAttrName(const char* path)
{
ASSERT(path != NULL);
char* name = strdup(path);
if (name == NULL)
return NULL;
char* current = strpbrk(name, "#$");
while (current != NULL) {
switch (*current) {
case '#':
*current = '/';
break;
case '$':
*current = ':';
break;
}
current = strpbrk(name, "#$");
}
return name;
}
status_t
Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
OpenDirCookie* cookie, uint64* _change, bool attribute)
{
ASSERT(_snapshot != NULL);
DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot;
if (snapshot == NULL)
return B_NO_MEMORY;
uint64 change = 0;
uint64 dirCookie = 0;
uint64 dirCookieVerf = 0;
bool eof = false;
while (!eof) {
uint32 count;
DirEntry* dirents;
status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change,
&dirCookie, &dirCookieVerf, attribute);
if (result != B_OK) {
delete snapshot;
return result;
}
uint32 i;
for (i = 0; i < count; i++) {
// FATTR4_FSID is mandatory
void* data = dirents[i].fAttrs[0].fData.fPointer;
FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data);
if (*fsid != fFileSystem->FsId())
continue;
if (strstr(dirents[i].fName, "-haiku-attrs") != NULL)
continue;
ino_t id;
if (!attribute) {
if (dirents[i].fAttrCount == 2)
id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
else
id = FileIdToInoT(fFileSystem->AllocFileId());
} else
id = 0;
const char* name = dirents[i].fName;
if (attribute)
name = FileToAttrName(name);
if (name == NULL) {
delete snapshot;
delete[] dirents;
return B_NO_MEMORY;
}
NameCacheEntry* entry = new NameCacheEntry(name, id);
if (attribute)
free(const_cast<char*>(name));
if (entry == NULL || entry->fName == NULL) {
if (entry != NULL)
delete entry;
delete snapshot;
delete[] dirents;
return B_NO_MEMORY;
}
snapshot->fEntries.Add(entry);
}
delete[] dirents;
}
*_snapshot = snapshot;
*_change = change;
return B_OK;
}
status_t
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
OpenDirCookie* cookie)
{
ASSERT(_buffer != NULL);
ASSERT(_count != NULL);
ASSERT(cookie != NULL);
if (cookie->fEOF) {
*_count = 0;
return B_OK;
}
status_t result;
DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache;
if (cookie->fSnapshot == NULL) {
cache->Lock();
result = cache->Revalidate();
if (result != B_OK) {
cache->Unlock();
return result;
}
DirectoryCacheSnapshot* snapshot;
result = cache->GetSnapshot(&snapshot);
if (result != B_OK) {
cache->Unlock();
return result;
}
cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot);
cache->Unlock();
if (cookie->fSnapshot == NULL)
return B_NO_MEMORY;
}
char* buffer = reinterpret_cast<char*>(_buffer);
uint32 pos = 0;
uint32 i = 0;
bool overflow = false;
if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
status_t result;
result = FillDirEntry(de, fInfo.fFileId, ".", pos, size);
if (result == B_BUFFER_OVERFLOW)
overflow = true;
else if (result == B_OK) {
pos += de->d_reclen;
i++;
cookie->fSpecial++;
} else
return result;
}
if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
status_t result;
result = ReadDirUp(de, pos, size);
if (result == B_ENTRY_NOT_FOUND) {
result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos,
size);
}
if (result == B_BUFFER_OVERFLOW)
overflow = true;
else if (result == B_OK) {
pos += de->d_reclen;
i++;
cookie->fSpecial++;
} else
return result;
}
MutexLocker _(cookie->fSnapshot->fLock);
for (; !overflow && i < *_count; i++) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
NameCacheEntry* temp = cookie->fCurrent;
if (cookie->fCurrent == NULL)
cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
else {
cookie->fCurrent
= cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
}
if (cookie->fCurrent == NULL) {
cookie->fEOF = true;
break;
}
if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
pos, size) == B_BUFFER_OVERFLOW) {
cookie->fCurrent = temp;
overflow = true;
break;
}
pos += de->d_reclen;
}
if (i == 0 && overflow)
return B_BUFFER_OVERFLOW;
*_count = i;
return B_OK;
}
@@ -0,0 +1,126 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef INODEIDMAP_H
#define INODEIDMAP_H
#include <lock.h>
#include <SupportDefs.h>
#include <util/AutoLock.h>
#include <util/AVLTreeMap.h>
#include "FileInfo.h"
struct InodeIdMapEntry {
FileInfo fFileInfo;
bool fRemoved;
};
class InodeIdMap {
public:
inline InodeIdMap();
inline ~InodeIdMap();
inline status_t AddEntry(const FileInfo& fi,
ino_t id, bool weak = false);
inline status_t MarkRemoved(ino_t id);
inline status_t RemoveEntry(ino_t id);
inline status_t GetFileInfo(FileInfo* fi, ino_t id);
protected:
inline bool _IsEntryRemoved(ino_t id);
private:
AVLTreeMap<ino_t, InodeIdMapEntry> fMap;
mutex fLock;
};
inline
InodeIdMap::InodeIdMap()
{
mutex_init(&fLock, NULL);
}
inline
InodeIdMap::~InodeIdMap()
{
mutex_destroy(&fLock);
}
inline status_t
InodeIdMap::AddEntry(const FileInfo& fi, ino_t id, bool weak)
{
InodeIdMapEntry entry;
MutexLocker _(fLock);
if (!weak || _IsEntryRemoved(id))
fMap.Remove(id);
entry.fFileInfo = fi;
entry.fRemoved = false;
return fMap.Insert(id, entry);
}
inline status_t
InodeIdMap::MarkRemoved(ino_t id)
{
MutexLocker _(fLock);
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
if (!it.HasCurrent())
return B_ENTRY_NOT_FOUND;
it.CurrentValuePointer()->fRemoved = true;
return B_OK;
}
inline status_t
InodeIdMap::RemoveEntry(ino_t id)
{
MutexLocker _(fLock);
if (_IsEntryRemoved(id))
return fMap.Remove(id);
return B_OK;
}
inline status_t
InodeIdMap::GetFileInfo(FileInfo* fi, ino_t id)
{
ASSERT(fi != NULL);
MutexLocker _(fLock);
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
if (!it.HasCurrent())
return B_ENTRY_NOT_FOUND;
*fi = it.Current().fFileInfo;
return B_OK;
}
// Caller must hold fLock
inline bool
InodeIdMap::_IsEntryRemoved(ino_t id)
{
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
if (!it.HasCurrent())
return true;
return it.Current().fRemoved;
}
#endif // INODEIDMAP_H
@@ -0,0 +1,446 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Inode.h"
#include <string.h>
#include <AutoDeleter.h>
#include <fs_cache.h>
#include <NodeMonitor.h>
#include "IdMap.h"
#include "Request.h"
#include "RootInode.h"
status_t
Inode::CreateState(const char* name, int mode, int perms, OpenState* state,
OpenDelegationData* delegationData) {
ASSERT(name != NULL);
ASSERT(state != NULL);
ASSERT(delegationData != NULL);
uint64 fileID;
FileHandle handle;
ChangeInfo changeInfo;
status_t result = CreateFile(name, mode, perms, state, &changeInfo,
&fileID, &handle, delegationData);
if (result != B_OK)
return result;
FileInfo fi;
fi.fFileId = fileID;
fi.fHandle = handle;
fi.fParent = fInfo.fHandle;
fi.CreateName(fInfo.fPath, name);
fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID));
fCache->Lock();
if (fCache->Valid()) {
if (changeInfo.fAtomic
&& fCache->ChangeInfo() == changeInfo.fBefore) {
fCache->AddEntry(name, fileID, true);
fCache->SetChangeInfo(changeInfo.fAfter);
} else
fCache->Trash();
}
fCache->Unlock();
state->fFileSystem = fFileSystem;
state->fInfo = fi;
state->fMode = mode & O_RWMASK;
return B_OK;
}
status_t
Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
OpenDelegationData* data, ino_t* id)
{
ASSERT(name != NULL);
ASSERT(cookie != NULL);
ASSERT(data != NULL);
cookie->fMode = mode;
cookie->fLocks = NULL;
OpenState* state = new OpenState;
status_t result = CreateState(name, mode, perms, state, data);
if (result != B_OK)
return result;
cookie->fOpenState = state;
cookie->fFileSystem = fFileSystem;
*id = FileIdToInoT(state->fInfo.fFileId);
fFileSystem->AddOpenFile(state);
fFileSystem->Root()->MakeInfoInvalid();
notify_entry_created(fFileSystem->DevId(), ID(), name, *id);
return B_OK;
}
status_t
Inode::Open(int mode, OpenFileCookie* cookie)
{
ASSERT(cookie != NULL);
MutexLocker locker(fStateLock);
OpenDelegationData data;
data.fType = OPEN_DELEGATE_NONE;
if (fOpenState == NULL) {
RevalidateFileCache();
OpenState* state = new OpenState;
if (state == NULL)
return B_NO_MEMORY;
state->fInfo = fInfo;
state->fFileSystem = fFileSystem;
state->fMode = mode & O_RWMASK;
status_t result = OpenFile(state, mode, &data);
if (result != B_OK)
return result;
fFileSystem->AddOpenFile(state);
fOpenState = state;
cookie->fOpenState = state;
locker.Unlock();
} else {
fOpenState->AcquireReference();
cookie->fOpenState = fOpenState;
locker.Unlock();
int newMode = mode & O_RWMASK;
int oldMode = fOpenState->fMode & O_RWMASK;
if (oldMode != newMode && oldMode != O_RDWR) {
if (oldMode == O_RDONLY)
RecallReadDelegation();
status_t result = OpenFile(fOpenState, O_RDWR, &data);
if (result != B_OK) {
locker.Lock();
ReleaseOpenState();
return result;
}
fOpenState->fMode = O_RDWR;
} else {
int newMode = mode & O_RWMASK;
uint32 allowed = 0;
if (newMode == O_RDWR || newMode == O_RDONLY)
allowed |= R_OK;
if (newMode == O_RDWR || newMode == O_WRONLY)
allowed |= W_OK;
status_t result = Access(allowed);
if (result != B_OK) {
locker.Lock();
ReleaseOpenState();
return result;
}
}
}
if ((mode & O_TRUNC) == O_TRUNC) {
struct stat st;
st.st_size = 0;
WriteStat(&st, B_STAT_SIZE);
file_cache_set_size(fFileCache, 0);
}
cookie->fFileSystem = fFileSystem;
cookie->fMode = mode;
cookie->fLocks = NULL;
if (data.fType != OPEN_DELEGATE_NONE) {
Delegation* delegation
= new(std::nothrow) Delegation(data, this, fOpenState->fClientID);
if (delegation != NULL) {
delegation->fInfo = fOpenState->fInfo;
delegation->fFileSystem = fFileSystem;
SetDelegation(delegation);
}
}
return B_OK;
}
status_t
Inode::Close(OpenFileCookie* cookie)
{
ASSERT(cookie != NULL);
ASSERT(fOpenState == cookie->fOpenState);
SyncAndCommit();
MutexLocker _(fStateLock);
ReleaseOpenState();
return B_OK;
}
char*
Inode::AttrToFileName(const char* path)
{
ASSERT(path != NULL);
char* name = strdup(path);
if (name == NULL)
return NULL;
char* current = strpbrk(name, "/:");
while (current != NULL) {
switch (*current) {
case '/':
*current = '#';
break;
case ':':
*current = '$';
break;
}
current = strpbrk(name, "/:");
}
return name;
}
status_t
Inode::OpenAttr(const char* _name, int mode, OpenAttrCookie* cookie,
bool create, int32 type)
{
ASSERT(_name != NULL);
ASSERT(cookie != NULL);
(void)type;
status_t result = LoadAttrDirHandle();
if (result != B_OK)
return result;
char* name = AttrToFileName(_name);
if (name == NULL)
return B_NO_MEMORY;
MemoryDeleter nameDeleter(name);
OpenDelegationData data;
data.fType = OPEN_DELEGATE_NONE;
OpenState* state = new OpenState;
if (state == NULL)
return B_NO_MEMORY;
state->fInfo.fName = strdup(name);
state->fInfo.fParent = fInfo.fAttrDir;
state->fFileSystem = fFileSystem;
result = NFS4Inode::OpenAttr(state, name, mode, &data, create);
if (result != B_OK) {
delete state;
return result;
}
fFileSystem->AddOpenFile(state);
cookie->fOpenState = state;
cookie->fFileSystem = fFileSystem;
cookie->fMode = mode;
if (data.fType != OPEN_DELEGATE_NONE) {
Delegation* delegation
= new(std::nothrow) Delegation(data, this, state->fClientID, true);
if (delegation != NULL) {
delegation->fInfo = state->fInfo;
delegation->fFileSystem = fFileSystem;
state->fDelegation = delegation;
fFileSystem->AddDelegation(delegation);
}
}
if (create || (mode & O_TRUNC) == O_TRUNC) {
struct stat st;
st.st_size = 0;
WriteStat(&st, B_STAT_SIZE, cookie);
}
return B_OK;
}
status_t
Inode::CloseAttr(OpenAttrCookie* cookie)
{
ASSERT(cookie != NULL);
if (cookie->fOpenState->fDelegation != NULL) {
cookie->fOpenState->fDelegation->GiveUp();
fFileSystem->RemoveDelegation(cookie->fOpenState->fDelegation);
}
delete cookie->fOpenState->fDelegation;
delete cookie->fOpenState;
return B_OK;
}
status_t
Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer,
size_t* _length, bool* eof)
{
ASSERT(cookie != NULL || fOpenState != NULL);
ASSERT(buffer != NULL);
ASSERT(_length != NULL);
ASSERT(eof != NULL);
*eof = false;
uint32 size = 0;
uint32 ioSize = fFileSystem->Root()->IOSize();
*_length = min_c(ioSize, *_length);
status_t result;
OpenState* state = cookie != NULL ? cookie->fOpenState : fOpenState;
while (size < *_length && !*eof) {
uint32 len = *_length - size;
result = ReadFile(cookie, state, pos + size, &len,
reinterpret_cast<char*>(buffer) + size, eof);
if (result != B_OK) {
if (size == 0)
return result;
else
break;
}
size += len;
}
*_length = size;
return B_OK;
}
status_t
Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
{
ASSERT(cookie != NULL);
ASSERT(buffer != NULL);
ASSERT(_length != NULL);
bool eof = false;
if ((cookie->fMode & O_NOCACHE) != 0)
return ReadDirect(cookie, pos, buffer, _length, &eof);
return file_cache_read(fFileCache, cookie, pos, buffer, _length);
}
status_t
Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer,
size_t* _length)
{
ASSERT(cookie != NULL || fOpenState != NULL);
ASSERT(_buffer != NULL);
ASSERT(_length != NULL);
uint32 size = 0;
const char* buffer = reinterpret_cast<const char*>(_buffer);
uint32 ioSize = fFileSystem->Root()->IOSize();
*_length = min_c(ioSize, *_length);
bool attribute = false;
OpenState* state = fOpenState;
if (cookie != NULL) {
attribute = cookie->fOpenState->fInfo.fHandle != fInfo.fHandle;
state = cookie->fOpenState;
}
if (!attribute) {
ReadLocker _(fWriteLock);
fWriteDirty = true;
}
while (size < *_length) {
uint32 len = *_length - size;
status_t result = WriteFile(cookie, state, pos + size, &len,
buffer + size, attribute);
if (result != B_OK) {
if (size == 0)
return result;
else
break;
}
size += len;
}
*_length = size;
fMetaCache.GrowFile(size + pos);
fFileSystem->Root()->MakeInfoInvalid();
return B_OK;
}
status_t
Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
size_t* _length)
{
ASSERT(cookie != NULL);
ASSERT(_buffer != NULL);
ASSERT(_length != NULL);
struct stat st;
status_t result = Stat(&st);
if (result != B_OK)
return result;
if ((cookie->fMode & O_APPEND) != 0)
pos = st.st_size;
uint64 fileSize = max_c(st.st_size, pos + *_length);
fMaxFileSize = max_c(fMaxFileSize, fileSize);
if ((cookie->fMode & O_NOCACHE) != 0) {
WriteDirect(cookie, pos, _buffer, _length);
Commit();
}
result = file_cache_set_size(fFileCache, fileSize);
if (result != B_OK)
return result;
return file_cache_write(fFileCache, cookie, pos, _buffer, _length);
}
status_t
Inode::Commit()
{
WriteLocker _(fWriteLock);
if (!fWriteDirty)
return B_OK;
status_t result = CommitWrites();
if (result != B_OK)
return result;
fWriteDirty = false;
return B_OK;
}
@@ -0,0 +1,43 @@
SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 ;
UsePrivateKernelHeaders ;
UsePrivateHeaders shared ;
KernelAddon nfs4 :
Cookie.cpp
Connection.cpp
Delegation.cpp
DirectoryCache.cpp
FileInfo.cpp
FileSystem.cpp
IdMap.cpp
Inode.cpp
InodeDir.cpp
InodeRegular.cpp
kernel_interface.cpp
MetadataCache.cpp
NFS4Inode.cpp
NFS4Object.cpp
NFS4Server.cpp
OpenState.cpp
ReplyBuilder.cpp
ReplyInterpreter.cpp
Request.cpp
RequestBuilder.cpp
RequestInterpreter.cpp
RootInode.cpp
RPCAuth.cpp
RPCCall.cpp
RPCCallback.cpp
RPCCallbackReply.cpp
RPCCallbackRequest.cpp
RPCCallbackServer.cpp
RPCReply.cpp
RPCServer.cpp
VnodeToInode.cpp
WorkQueue.cpp
XDR.cpp
;
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;
@@ -0,0 +1,180 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "MetadataCache.h"
#include <NodeMonitor.h>
#include "Inode.h"
MetadataCache::MetadataCache(Inode* inode)
:
fExpire(0),
fForceValid(false),
fInode(inode),
fInited(false)
{
ASSERT(inode != NULL);
mutex_init(&fLock, NULL);
}
MetadataCache::~MetadataCache()
{
mutex_destroy(&fLock);
}
status_t
MetadataCache::GetStat(struct stat* st)
{
ASSERT(st != NULL);
MutexLocker _(fLock);
if (fForceValid || fExpire > time(NULL)) {
// Do not touch other members of struct stat
st->st_size = fStatCache.st_size;
st->st_mode = fStatCache.st_mode;
st->st_nlink = fStatCache.st_nlink;
st->st_uid = fStatCache.st_uid;
st->st_gid = fStatCache.st_gid;
st->st_atim = fStatCache.st_atim;
st->st_ctim = fStatCache.st_ctim;
st->st_crtim = fStatCache.st_crtim;
st->st_mtim = fStatCache.st_mtim;
st->st_blksize = fStatCache.st_blksize;
st->st_blocks = fStatCache.st_blocks;
return B_OK;
}
return B_ERROR;
}
void
MetadataCache::SetStat(const struct stat& st)
{
MutexLocker _(fLock);
if (fInited)
NotifyChanges(&fStatCache, &st);
fStatCache = st;
fExpire = time(NULL) + kExpirationTime;
fInited = true;
}
void
MetadataCache::GrowFile(size_t newSize)
{
MutexLocker _(fLock);
fStatCache.st_size = max_c(newSize, fStatCache.st_size);
}
status_t
MetadataCache::GetAccess(uid_t uid, uint32* allowed)
{
ASSERT(allowed != NULL);
MutexLocker _(fLock);
AVLTreeMap<uid_t, AccessEntry>::Iterator it = fAccessCache.Find(uid);
if (!it.HasCurrent())
return B_ENTRY_NOT_FOUND;
if (!fForceValid)
it.CurrentValuePointer()->fForceValid = false;
if (!it.Current().fForceValid && it.Current().fExpire < time(NULL)) {
it.Remove();
return B_ERROR;
}
*allowed = it.Current().fAllowed;
return B_OK;
}
void
MetadataCache::SetAccess(uid_t uid, uint32 allowed)
{
MutexLocker _(fLock);
AVLTreeMap<uid_t, AccessEntry>::Iterator it = fAccessCache.Find(uid);
if (it.HasCurrent())
it.Remove();
AccessEntry entry;
entry.fAllowed = allowed;
entry.fExpire = time(NULL) + kExpirationTime;
entry.fForceValid = fForceValid;
fAccessCache.Insert(uid, entry);
}
status_t
MetadataCache::LockValid()
{
MutexLocker _(fLock);
if (fForceValid || fExpire > time(NULL)) {
fForceValid = true;
return B_OK;
}
return B_ERROR;
}
void
MetadataCache::UnlockValid()
{
MutexLocker _(fLock);
fExpire = time(NULL) + kExpirationTime;
fForceValid = false;
}
void
MetadataCache::NotifyChanges(const struct stat* oldStat,
const struct stat* newStat)
{
ASSERT(oldStat != NULL);
ASSERT(newStat != NULL);
uint32 flags = 0;
if (oldStat->st_size != newStat->st_size)
flags |= B_STAT_SIZE;
if (oldStat->st_mode != newStat->st_mode)
flags |= B_STAT_MODE;
if (oldStat->st_uid != newStat->st_uid)
flags |= B_STAT_UID;
if (oldStat->st_gid != newStat->st_gid)
flags |= B_STAT_GID;
if (memcmp(&oldStat->st_atim, &newStat->st_atim,
sizeof(struct timespec) == 0))
flags |= B_STAT_ACCESS_TIME;
if (memcmp(&oldStat->st_ctim, &newStat->st_ctim,
sizeof(struct timespec) == 0))
flags |= B_STAT_CHANGE_TIME;
if (memcmp(&oldStat->st_crtim, &newStat->st_crtim,
sizeof(struct timespec) == 0))
flags |= B_STAT_CREATION_TIME;
if (memcmp(&oldStat->st_mtim, &newStat->st_mtim,
sizeof(struct timespec) == 0))
flags |= B_STAT_MODIFICATION_TIME;
notify_stat_changed(fInode->GetFileSystem()->DevId(), fInode->ID(), flags);
}
@@ -0,0 +1,95 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef METADATACACHE_H
#define METADATACACHE_H
#include <fs_interface.h>
#include <lock.h>
#include <SupportDefs.h>
#include <util/AutoLock.h>
#include <util/AVLTreeMap.h>
class Inode;
struct AccessEntry {
time_t fExpire;
bool fForceValid;
uint32 fAllowed;
};
class MetadataCache {
public:
MetadataCache(Inode* inode);
~MetadataCache();
status_t GetStat(struct stat* st);
void SetStat(const struct stat& st);
void GrowFile(size_t newSize);
status_t GetAccess(uid_t uid, uint32* allowed);
void SetAccess(uid_t uid, uint32 allowed);
status_t LockValid();
void UnlockValid();
inline void InvalidateStat();
inline void InvalidateAccess();
inline void Invalidate();
static const time_t kExpirationTime = 60;
protected:
void NotifyChanges(const struct stat* oldStat,
const struct stat* newStat);
private:
struct stat fStatCache;
time_t fExpire;
bool fForceValid;
Inode* fInode;
bool fInited;
AVLTreeMap<uid_t, AccessEntry> fAccessCache;
mutex fLock;
};
inline void
MetadataCache::InvalidateStat()
{
MutexLocker _(fLock);
if (!fForceValid)
fExpire = 0;
}
inline void
MetadataCache::InvalidateAccess()
{
MutexLocker _(fLock);
if (!fForceValid)
fAccessCache.MakeEmpty();
}
inline void
MetadataCache::Invalidate()
{
InvalidateStat();
InvalidateAccess();
}
#endif // METADATACACHE_H
@@ -0,0 +1,371 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef NFS4DEFS_H
#define NFS4DEFS_H
#include <fcntl.h>
#include <sys/stat.h>
#include <SupportDefs.h>
enum Procedure {
ProcNull = 0,
ProcCompound = 1
};
enum CallbackProcedure {
CallbackProcNull = 0,
CallbackProcCompound = 1
};
enum CallbackOpcode {
OpCallbackGetAttr = 3,
OpCallbackRecall = 4
};
enum Opcode {
OpAccess = 3,
OpClose = 4,
OpCommit = 5,
OpCreate = 6,
OpDelegReturn = 8,
OpGetAttr = 9,
OpGetFH = 10,
OpLink = 11,
OpLock = 12,
OpLockT = 13,
OpLockU = 14,
OpLookUp = 15,
OpLookUpUp = 16,
OpNverify = 17,
OpOpen = 18,
OpOpenAttrDir = 19,
OpOpenConfirm = 20,
OpPutFH = 22,
OpPutRootFH = 24,
OpRead = 25,
OpReadDir = 26,
OpReadLink = 27,
OpRemove = 28,
OpRename = 29,
OpRenew = 30,
OpSaveFH = 32,
OpSetAttr = 34,
OpSetClientID = 35,
OpSetClientIDConfirm = 36,
OpVerify = 37,
OpWrite = 38,
OpReleaseLockOwner = 39
};
enum Access {
ACCESS4_READ = 0x00000001,
ACCESS4_LOOKUP = 0x00000002,
ACCESS4_MODIFY = 0x00000004,
ACCESS4_EXTEND = 0x00000008,
ACCESS4_DELETE = 0x00000010,
ACCESS4_EXECUTE = 0x00000020
};
enum Attribute {
// Mandatory Attributes
FATTR4_SUPPORTED_ATTRS = 0,
FATTR4_TYPE = 1,
FATTR4_FH_EXPIRE_TYPE = 2,
FATTR4_CHANGE = 3,
FATTR4_SIZE = 4,
FATTR4_LINK_SUPPORT = 5,
FATTR4_SYMLINK_SUPPORT = 6,
FATTR4_NAMED_ATTR = 7,
FATTR4_FSID = 8,
FATTR4_UNIQUE_HANDLES = 9,
FATTR4_LEASE_TIME = 10,
FATTR4_RDATTR_ERROR = 11,
FATTR4_FILEHANDLE = 19,
// Recommended Attributes
FATTR4_ACL = 12,
FATTR4_ACLSUPPORT = 13,
FATTR4_ARCHIVE = 14,
FATTR4_CANSETTIME = 15,
FATTR4_CASE_INSENSITIVE = 16,
FATTR4_CASE_PRESERVING = 17,
FATTR4_CHOWN_RESTRICTED = 18,
FATTR4_FILEID = 20,
FATTR4_FILES_AVAIL = 21,
FATTR4_FILES_FREE = 22,
FATTR4_FILES_TOTAL = 23,
FATTR4_FS_LOCATIONS = 24,
FATTR4_HIDDEN = 25,
FATTR4_HOMOGENEOUS = 26,
FATTR4_MAXFILESIZE = 27,
FATTR4_MAXLINK = 28,
FATTR4_MAXNAME = 29,
FATTR4_MAXREAD = 30,
FATTR4_MAXWRITE = 31,
FATTR4_MIMETYPE = 32,
FATTR4_MODE = 33,
FATTR4_NO_TRUNC = 34,
FATTR4_NUMLINKS = 35,
FATTR4_OWNER = 36,
FATTR4_OWNER_GROUP = 37,
FATTR4_QUOTA_AVAIL_HARD = 38,
FATTR4_QUOTA_AVAIL_SOFT = 39,
FATTR4_QUOTA_USED = 40,
FATTR4_RAWDEV = 41,
FATTR4_SPACE_AVAIL = 42,
FATTR4_SPACE_FREE = 43,
FATTR4_SPACE_TOTAL = 44,
FATTR4_SPACE_USED = 45,
FATTR4_SYSTEM = 46,
FATTR4_TIME_ACCESS = 47,
FATTR4_TIME_ACCESS_SET = 48,
FATTR4_TIME_BACKUP = 49,
FATTR4_TIME_CREATE = 50,
FATTR4_TIME_DELTA = 51,
FATTR4_TIME_METADATA = 52,
FATTR4_TIME_MODIFY = 53,
FATTR4_TIME_MODIFY_SET = 54,
FATTR4_MOUNTED_ON_FILEID = 55,
FATTR4_MAXIMUM_ATTR_ID
};
enum CallbackAttr {
CallbackAttrSize = 1,
CallbackAttrChange = 2
};
static inline bool sIsAttrSet(Attribute attr, const uint32* bitmap,
uint32 count)
{
if ((uint32)attr / 32 >= count)
return false;
return (bitmap[attr / 32] & 1 << attr % 32) != 0;
}
enum FileType {
NF4REG = 1, /* Regular File */
NF4DIR = 2, /* Directory */
NF4BLK = 3, /* Special File - block device */
NF4CHR = 4, /* Special File - character device */
NF4LNK = 5, /* Symbolic Link */
NF4SOCK = 6, /* Special File - socket */
NF4FIFO = 7, /* Special File - fifo */
NF4ATTRDIR = 8, /* Attribute Directory */
NF4NAMEDATTR = 9 /* Named Attribute */
};
static const mode_t sNFSFileTypeToHaiku[] = {
S_IFREG, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO,
S_IFDIR, S_IFREG
};
enum FileHandleExpiryType {
FH4_PERSISTENT = 0x00,
FH4_NOEXPIRE_WITH_OPEN = 0x01,
FH4_VOLATILE_ANY = 0x02,
FH4_VOL_MIGRATION = 0x04,
FH4_VOL_RENAME = 0x08
};
enum OpenAccess {
OPEN4_SHARE_ACCESS_READ = 1,
OPEN4_SHARE_ACCESS_WRITE = 2,
OPEN4_SHARE_ACCESS_BOTH = 3
};
static inline OpenAccess
sModeToAccess(int mode)
{
switch (mode & O_RWMASK) {
case O_RDONLY:
return OPEN4_SHARE_ACCESS_READ;
case O_WRONLY:
return OPEN4_SHARE_ACCESS_WRITE;
case O_RDWR:
return OPEN4_SHARE_ACCESS_BOTH;
}
return OPEN4_SHARE_ACCESS_READ;
}
enum OpenCreate {
OPEN4_NOCREATE = 0,
OPEN4_CREATE = 1
};
enum OpenCreateHow {
UNCHECKED4 = 0,
GUARDED4 = 1,
EXCLUSIVE4 = 2
};
enum OpenClaim {
CLAIM_NULL = 0,
CLAIM_PREVIOUS = 1,
CLAIM_DELEGATE_CUR = 2,
CLAIM_DELEGATE_PREV = 3
};
enum OpenDelegation {
OPEN_DELEGATE_NONE = 0,
OPEN_DELEGATE_READ = 1,
OPEN_DELEGATE_WRITE = 2
};
struct OpenDelegationData {
OpenDelegation fType;
uint32 fStateSeq;
uint32 fStateID[3];
bool fRecall;
uint64 fSpaceLimit;
};
enum OpenFlags {
OPEN4_RESULT_CONFIRM = 2,
OPEN4_RESULT_LOCKTYPE_POSIX = 4
};
enum {
NFS_LIMIT_SIZE = 1,
NFS_LIMIT_BLOCKS = 2
};
struct ChangeInfo {
bool fAtomic;
uint64 fBefore;
uint64 fAfter;
};
enum WriteStable {
UNSTABLE4 = 0,
DATA_SYNC4 = 1,
FILE_SYNC4 = 2
};
enum LockType {
READ_LT = 1,
WRITE_LT = 2,
READW_LT = 3,
WRITEW_LT = 4
};
static inline LockType
sGetLockType(short type, bool wait) {
switch (type) {
case F_RDLCK: return wait ? READW_LT : READ_LT;
case F_WRLCK: return wait ? WRITEW_LT : WRITE_LT;
default: return READ_LT;
}
}
static inline short
sLockTypeToHaiku(LockType type) {
switch (type) {
case READ_LT:
case READW_LT:
return F_RDLCK;
case WRITE_LT:
case WRITEW_LT:
return F_WRLCK;
default: return F_UNLCK;
}
}
enum Errors {
NFS4_OK = 0,
NFS4ERR_PERM = 1,
NFS4ERR_NOENT = 2,
NFS4ERR_IO = 5,
NFS4ERR_NXIO = 6,
NFS4ERR_ACCESS = 13,
NFS4ERR_EXIST = 17,
NFS4ERR_XDEV = 18,
NFS4ERR_NOTDIR = 20,
NFS4ERR_ISDIR = 21,
NFS4ERR_INVAL = 22,
NFS4ERR_FBIG = 27,
NFS4ERR_NOSPC = 28,
NFS4ERR_ROFS = 30,
NFS4ERR_MLINK = 31,
NFS4ERR_NAMETOOLONG = 63,
NFS4ERR_NOTEMPTY = 66,
NFS4ERR_DQUOT = 69,
NFS4ERR_STALE = 70,
NFS4ERR_BADHANDLE = 10001,
NFS4ERR_BAD_COOKIE = 10003,
NFS4ERR_NOTSUPP = 10004,
NFS4ERR_TOOSMALL = 10005,
NFS4ERR_SERVERFAULT = 10006,
NFS4ERR_BADTYPE = 10007,
NFS4ERR_DELAY = 10008,
NFS4ERR_SAME = 10009,
NFS4ERR_DENIED = 10010,
NFS4ERR_EXPIRED = 10011,
NFS4ERR_LOCKED = 10012,
NFS4ERR_GRACE = 10013,
NFS4ERR_FHEXPIRED = 10014,
NFS4ERR_SHARE_DENIED = 10015,
NFS4ERR_WRONGSEC = 10016,
NFS4ERR_CLID_INUSE = 10017,
NFS4ERR_RESOURCE = 10018,
NFS4ERR_MOVED = 10019,
NFS4ERR_NOFILEHANDLE = 10020,
NFS4ERR_MINOR_VERS_MISMATCH = 10021,
NFS4ERR_STALE_CLIENTID = 10022,
NFS4ERR_STALE_STATEID = 10023,
NFS4ERR_OLD_STATEID = 10024,
NFS4ERR_BAD_STATEID = 10025,
NFS4ERR_BAD_SEQID = 10026,
NFS4ERR_NOT_SAME = 10027,
NFS4ERR_LOCK_RANGE = 10028,
NFS4ERR_SYMLINK = 10029,
NFS4ERR_RESTOREFH = 10030,
NFS4ERR_LEASE_MOVED = 10031,
NFS4ERR_ATTRNOTSUPP = 10032,
NFS4ERR_NO_GRACE = 10033,
NFS4ERR_RECLAIM_BAD = 10034,
NFS4ERR_RECLAIM_CONFLICT = 10035,
NFS4ERR_BADXDR = 10036,
NFS4ERR_LOCKS_HELD = 10037,
NFS4ERR_OPENMODE = 10038,
NFS4ERR_BADOWNER = 10039,
NFS4ERR_BADCHAR = 10040,
NFS4ERR_BADNAME = 10041,
NFS4ERR_BAD_RANGE = 10042,
NFS4ERR_LOCK_NOTSUPP = 10043,
NFS4ERR_OP_ILLEGAL = 10044,
NFS4ERR_DEADLOCK = 10045,
NFS4ERR_FILE_OPEN = 10046,
NFS4ERR_ADMIN_REVOKED = 10047,
NFS4ERR_CB_PATH_DOWN = 10048
};
static inline bigtime_t
sSecToBigTime(uint32 sec)
{
return static_cast<bigtime_t>(sec) * 1000000;
}
#endif // NFS4DEFS_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef NFS4INODE_H
#define NFS4INODE_H
#include <sys/stat.h>
#include <SupportDefs.h>
#include "Cookie.h"
#include "FileInfo.h"
#include "FileSystem.h"
#include "NFS4Object.h"
#include "ReplyInterpreter.h"
class NFS4Inode : public NFS4Object {
public:
status_t GetChangeInfo(uint64* change, bool attrDir = false);
status_t ReadLink(void* buffer, size_t* length);
protected:
status_t Access(uint32* allowed);
status_t CommitWrites();
status_t LookUp(const char* name, uint64* change, uint64* fileID,
FileHandle* handle, bool parent = false);
status_t Link(Inode* dir, const char* name,
ChangeInfo* changeInfo);
static status_t RenameNode(Inode* from, Inode* to, const char* fromName,
const char* toName, ChangeInfo* fromChange,
ChangeInfo* toChange, uint64* fileID,
bool attribute = false);
status_t GetStat(AttrValue** values, uint32* count,
OpenAttrCookie* attr = NULL);
status_t WriteStat(OpenState* state, AttrValue* attrs,
uint32 attrCount);
status_t CreateFile(const char* name, int mode, int perms,
OpenState* state, ChangeInfo* changeInfo,
uint64* fileID, FileHandle* handle,
OpenDelegationData* delegation);
status_t OpenFile(OpenState* state, int mode,
OpenDelegationData* delegation);
status_t OpenAttr(OpenState* state, const char* name, int mode,
OpenDelegationData* delegation, bool create);
status_t ReadFile(OpenStateCookie* cookie, OpenState* state,
uint64 position, uint32* length, void* buffer,
bool* eof);
status_t WriteFile(OpenStateCookie* cookie, OpenState* state,
uint64 position, uint32* length,
const void* buffer, bool commit = false);
status_t CreateObject(const char* name, const char* path,
int mode, FileType type, ChangeInfo* changeInfo,
uint64* fileID, FileHandle* handle,
bool parent = false);
status_t RemoveObject(const char* name, FileType type,
ChangeInfo* changeInfo, uint64* fileID);
status_t ReadDirOnce(DirEntry** dirents, uint32* count,
OpenDirCookie* cookie, bool* eof, uint64* change,
uint64* dirCookie, uint64* dirCookieVerf,
bool attribute);
status_t OpenAttrDir(FileHandle* handle);
status_t TestLock(OpenFileCookie* cookie, LockType* type,
uint64* position, uint64* length, bool& conflict);
status_t AcquireLock(OpenFileCookie* cookie, LockInfo* lockInfo,
bool wait);
status_t ReleaseLock(OpenFileCookie* cookie, LockInfo* lockInfo);
};
#endif // NFS4INODE_H
@@ -0,0 +1,204 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Cookie.h"
#include "FileSystem.h"
#include "NFS4Object.h"
#include "OpenState.h"
#include "Request.h"
bool
NFS4Object::HandleErrors(uint32 nfs4Error, RPC::Server* serv,
OpenStateCookie* cookie, OpenState* state, uint32* sequence)
{
uint32 leaseTime;
// No request send by the client should cause any of the following errors.
ASSERT(nfs4Error != NFS4ERR_CLID_INUSE);
ASSERT(nfs4Error != NFS4ERR_NOFILEHANDLE);
ASSERT(nfs4Error != NFS4ERR_BAD_STATEID);
ASSERT(nfs4Error != NFS4ERR_RESTOREFH);
ASSERT(nfs4Error != NFS4ERR_LOCKS_HELD);
ASSERT(nfs4Error != NFS4ERR_OP_ILLEGAL);
if (cookie != NULL)
state = cookie->fOpenState;
switch (nfs4Error) {
case NFS4_OK:
return false;
// retransmission of CLOSE caused seqid to fall back
case NFS4ERR_BAD_SEQID:
ASSERT(sequence != NULL);
(*sequence)++;
return true;
// server needs more time, we need to wait
case NFS4ERR_LOCKED:
case NFS4ERR_DELAY:
if (sequence != NULL)
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
if (cookie == NULL) {
snooze_etc(sSecToBigTime(5), B_SYSTEM_TIMEBASE,
B_RELATIVE_TIMEOUT);
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return true;
}
if ((cookie->fMode & O_NONBLOCK) == 0) {
status_t result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
B_RELATIVE_TIMEOUT, sSecToBigTime(5));
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
if (result != B_TIMED_OUT) {
if (result == B_OK)
release_sem(cookie->fSnoozeCancel);
return false;
}
return true;
}
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return false;
// server is in grace period, we need to wait
case NFS4ERR_GRACE:
leaseTime = fFileSystem->NFSServer()->LeaseTime();
if (sequence != NULL)
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
if (cookie == NULL) {
snooze_etc(sSecToBigTime(leaseTime) / 3, B_SYSTEM_TIMEBASE,
B_RELATIVE_TIMEOUT);
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return true;
}
if ((cookie->fMode & O_NONBLOCK) == 0) {
status_t result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
B_RELATIVE_TIMEOUT, sSecToBigTime(leaseTime) / 3);
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
if (result != B_TIMED_OUT) {
if (result == B_OK)
release_sem(cookie->fSnoozeCancel);
return false;
}
return true;
}
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return false;
// server has rebooted, reclaim share and try again
case NFS4ERR_STALE_CLIENTID:
case NFS4ERR_STALE_STATEID:
if (state != NULL) {
if (sequence != NULL)
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
fFileSystem->NFSServer()->ServerRebooted(state->fClientID);
if (sequence != NULL)
*sequence = fFileSystem->OpenOwnerSequenceLock();
return true;
}
return false;
// File Handle has expired, is invalid or the node has been deleted
case NFS4ERR_NOFILEHANDLE:
case NFS4ERR_BADHANDLE:
case NFS4ERR_FHEXPIRED:
case NFS4ERR_STALE:
if (fInfo.UpdateFileHandles(fFileSystem) == B_OK)
return true;
return false;
// filesystem has been moved
case NFS4ERR_LEASE_MOVED:
case NFS4ERR_MOVED:
fFileSystem->Migrate(serv);
return true;
// lease has expired
case NFS4ERR_EXPIRED:
if (state != NULL) {
fFileSystem->NFSServer()->ClientId(state->fClientID, true);
return true;
}
return false;
default:
return false;
}
}
status_t
NFS4Object::ConfirmOpen(const FileHandle& fh, OpenState* state,
uint32* sequence)
{
ASSERT(state != NULL);
ASSERT(sequence != NULL);
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fh);
req.OpenConfirm(*sequence, state->fStateID, state->fStateSeq);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
*sequence += IncrementSequence(reply.NFS4Error());
if (HandleErrors(reply.NFS4Error(), serv, NULL, state))
continue;
reply.PutFH();
result = reply.OpenConfirm(&state->fStateSeq);
if (result != B_OK)
return result;
return B_OK;
} while (true);
}
uint32
NFS4Object::IncrementSequence(uint32 error)
{
if (error != NFS4ERR_STALE_CLIENTID && error != NFS4ERR_STALE_STATEID
&& error != NFS4ERR_BAD_STATEID && error != NFS4ERR_BAD_SEQID
&& error != NFS4ERR_BADXDR && error != NFS4ERR_RESOURCE
&& error != NFS4ERR_NOFILEHANDLE)
return 1;
return 0;
}
@@ -0,0 +1,46 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef NFS4OBJECT_H
#define NFS4OBJECT_H
#include "FileInfo.h"
#include "NFS4Defs.h"
#include "RPCServer.h"
class OpenStateCookie;
class OpenState;
class NFS4Object {
public:
bool HandleErrors(uint32 nfs4Error, RPC::Server* serv,
OpenStateCookie* cookie = NULL,
OpenState* state = NULL, uint32* sequence = NULL);
status_t ConfirmOpen(const FileHandle& fileHandle,
OpenState* state, uint32* sequence);
static uint32 IncrementSequence(uint32 error);
inline NFS4Object();
FileInfo fInfo;
FileSystem* fFileSystem;
};
inline
NFS4Object::NFS4Object()
:
fFileSystem(NULL)
{
}
#endif // NFS4OBJECT_H
@@ -0,0 +1,425 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "FileSystem.h"
#include "Inode.h"
#include "NFS4Server.h"
#include "Request.h"
#include "WorkQueue.h"
NFS4Server::NFS4Server(RPC::Server* serv)
:
fThreadCancel(true),
fWaitCancel(create_sem(0, NULL)),
fLeaseTime(0),
fClientIdLastUse(0),
fUseCount(0),
fServer(serv)
{
ASSERT(serv != NULL);
mutex_init(&fClientIdLock, NULL);
mutex_init(&fFSLock, NULL);
mutex_init(&fThreadStartLock, NULL);
}
NFS4Server::~NFS4Server()
{
fThreadCancel = true;
fUseCount = 0;
release_sem(fWaitCancel);
status_t result;
wait_for_thread(fThread, &result);
delete_sem(fWaitCancel);
mutex_destroy(&fClientIdLock);
mutex_destroy(&fFSLock);
mutex_destroy(&fThreadStartLock);
}
uint64
NFS4Server::ServerRebooted(uint64 clientId)
{
if (clientId != fClientId)
return fClientId;
fClientId = ClientId(clientId, true);
// reclaim all opened files and held locks from all filesystems
MutexLocker _(fFSLock);
FileSystem* fs = fFileSystems.Head();
while (fs != NULL) {
DoublyLinkedList<OpenState>::Iterator iterator
= fs->OpenFilesLock().GetIterator();
OpenState* current = iterator.Next();
while (current != NULL) {
current->Reclaim(fClientId);
current = iterator.Next();
}
fs->OpenFilesUnlock();
fs = fFileSystems.GetNext(fs);
}
return fClientId;
}
void
NFS4Server::AddFileSystem(FileSystem* fs)
{
ASSERT(fs != NULL);
MutexLocker _(fFSLock);
fFileSystems.Add(fs);
fUseCount += fs->OpenFilesCount();
if (fs->OpenFilesCount() > 0)
_StartRenewing();
}
void
NFS4Server::RemoveFileSystem(FileSystem* fs)
{
ASSERT(fs != NULL);
MutexLocker _(fFSLock);
fFileSystems.Remove(fs);
fUseCount -= fs->OpenFilesCount();
}
uint64
NFS4Server::ClientId(uint64 prevId, bool forceNew)
{
MutexLocker _(fClientIdLock);
if ((fUseCount == 0 && fClientIdLastUse + (time_t)LeaseTime() < time(NULL))
|| (forceNew && fClientId == prevId)) {
Request request(fServer, NULL);
request.Builder().SetClientID(fServer);
status_t result = request.Send();
if (result != B_OK)
return fClientId;
uint64 ver;
result = request.Reply().SetClientID(&fClientId, &ver);
if (result != B_OK)
return fClientId;
request.Reset();
request.Builder().SetClientIDConfirm(fClientId, ver);
result = request.Send();
if (result != B_OK)
return fClientId;
result = request.Reply().SetClientIDConfirm();
if (result != B_OK)
return fClientId;
}
fClientIdLastUse = time(NULL);
return fClientId;
}
status_t
NFS4Server::FileSystemMigrated()
{
// reclaim all opened files and held locks from all filesystems
MutexLocker _(fFSLock);
FileSystem* fs = fFileSystems.Head();
while (fs != NULL) {
fs->Migrate(fServer);
fs = fFileSystems.GetNext(fs);
}
return B_OK;
}
status_t
NFS4Server::_GetLeaseTime()
{
Request request(fServer, NULL);
request.Builder().PutRootFH();
Attribute attr[] = { FATTR4_LEASE_TIME };
request.Builder().GetAttr(attr, sizeof(attr) / sizeof(Attribute));
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
reply.PutRootFH();
AttrValue* values;
uint32 count;
result = reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
// FATTR4_LEASE_TIME is mandatory
if (count < 1 || values[0].fAttribute != FATTR4_LEASE_TIME) {
delete[] values;
return B_BAD_VALUE;
}
fLeaseTime = values[0].fData.fValue32;
return B_OK;
}
status_t
NFS4Server::_StartRenewing()
{
if (!fThreadCancel)
return B_OK;
MutexLocker _(fThreadStartLock);
if (!fThreadCancel)
return B_OK;
if (fLeaseTime == 0) {
status_t result = _GetLeaseTime();
if (result != B_OK)
return result;
}
fThreadCancel = false;
fThread = spawn_kernel_thread(&NFS4Server::_RenewalThreadStart,
"NFSv4 Renewal", B_NORMAL_PRIORITY, this);
if (fThread < B_OK)
return fThread;
status_t result = resume_thread(fThread);
if (result != B_OK) {
kill_thread(fThread);
return result;
}
return B_OK;
}
status_t
NFS4Server::_Renewal()
{
while (!fThreadCancel) {
// TODO: operations like OPEN, READ, CLOSE, etc also renew leases
status_t result = acquire_sem_etc(fWaitCancel, 1,
B_RELATIVE_TIMEOUT, sSecToBigTime(fLeaseTime - 2));
if (result != B_TIMED_OUT) {
if (result == B_OK)
release_sem(fWaitCancel);
return B_OK;
}
uint64 clientId = fClientId;
if (fUseCount == 0) {
MutexLocker _(fFSLock);
if (fUseCount == 0) {
fThreadCancel = true;
return B_OK;
}
}
Request request(fServer, NULL);
request.Builder().Renew(clientId);
request.Send();
switch (request.Reply().NFS4Error()) {
case NFS4ERR_CB_PATH_DOWN:
RecallAll();
break;
case NFS4ERR_STALE_CLIENTID:
ServerRebooted(clientId);
break;
case NFS4ERR_LEASE_MOVED:
FileSystemMigrated();
break;
}
}
return B_OK;
}
status_t
NFS4Server::_RenewalThreadStart(void* ptr)
{
ASSERT(ptr != NULL);
NFS4Server* server = reinterpret_cast<NFS4Server*>(ptr);
return server->_Renewal();
}
status_t
NFS4Server::ProcessCallback(RPC::CallbackRequest* request,
Connection* connection)
{
ASSERT(request != NULL);
ASSERT(connection != NULL);
RequestInterpreter req(request);
ReplyBuilder reply(request->XID());
status_t result;
uint32 count = req.OperationCount();
for (uint32 i = 0; i < count; i++) {
switch (req.Operation()) {
case OpCallbackGetAttr:
result = CallbackGetAttr(&req, &reply);
break;
case OpCallbackRecall:
result = CallbackRecall(&req, &reply);
break;
default:
result = B_NOT_SUPPORTED;
}
if (result != B_OK)
break;
}
XDR::WriteStream& stream = reply.Reply()->Stream();
connection->Send(stream.Buffer(), stream.Size());
return B_OK;
}
status_t
NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply)
{
ASSERT(request != NULL);
ASSERT(reply != NULL);
uint32 stateID[3];
uint32 stateSeq;
bool truncate;
FileHandle handle;
status_t result = request->Recall(&handle, truncate, &stateSeq, stateID);
if (result != B_OK)
return result;
MutexLocker locker(fFSLock);
Delegation* delegation = NULL;
FileSystem* current = fFileSystems.Head();
while (current != NULL) {
delegation = current->GetDelegation(handle);
if (delegation != NULL)
break;
current = fFileSystems.GetNext(current);
}
locker.Unlock();
if (delegation == NULL) {
reply->Recall(B_FILE_NOT_FOUND);
return B_FILE_NOT_FOUND;
}
DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
args->fDelegation = delegation;
args->fTruncate = truncate;
gWorkQueue->EnqueueJob(DelegationRecall, args);
reply->Recall(B_OK);
return B_OK;
}
status_t
NFS4Server::CallbackGetAttr(RequestInterpreter* request, ReplyBuilder* reply)
{
ASSERT(request != NULL);
ASSERT(reply != NULL);
FileHandle handle;
int mask;
status_t result = request->GetAttr(&handle, &mask);
if (result != B_OK)
return result;
return B_OK;
MutexLocker locker(fFSLock);
Delegation* delegation = NULL;
FileSystem* current = fFileSystems.Head();
while (current != NULL) {
delegation = current->GetDelegation(handle);
if (delegation != NULL)
break;
current = fFileSystems.GetNext(current);
}
locker.Unlock();
if (delegation == NULL) {
reply->GetAttr(B_FILE_NOT_FOUND, 0, 0, 0);
return B_FILE_NOT_FOUND;
}
struct stat st;
delegation->GetInode()->Stat(&st);
uint64 change;
change = delegation->GetInode()->Change();
if (delegation->GetInode()->Dirty())
change++;
reply->GetAttr(B_OK, mask, st.st_size, change);
return B_OK;
}
status_t
NFS4Server::RecallAll()
{
MutexLocker _(fFSLock);
FileSystem* fs = fFileSystems.Head();
while (fs != NULL) {
DoublyLinkedList<Delegation>& list = fs->DelegationsLock();
DoublyLinkedList<Delegation>::Iterator iterator = list.GetIterator();
Delegation* current = iterator.Next();
while (current != NULL) {
DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
args->fDelegation = current;
args->fTruncate = false;
gWorkQueue->EnqueueJob(DelegationRecall, args);
current = iterator.Next();
}
fs->DelegationsUnlock();
fs = fFileSystems.GetNext(fs);
}
return B_OK;
}
@@ -0,0 +1,106 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef NFS4SERVER_H
#define NFS4SERVER_H
#include <lock.h>
#include "ReplyBuilder.h"
#include "RequestInterpreter.h"
#include "RPCServer.h"
class FileSystem;
class OpenFileCookie;
class NFS4Server : public RPC::ProgramData {
public:
NFS4Server(RPC::Server* serv);
virtual ~NFS4Server();
uint64 ServerRebooted(uint64 clientId);
status_t FileSystemMigrated();
void AddFileSystem(FileSystem* fs);
void RemoveFileSystem(FileSystem* fs);
inline void IncUsage();
inline void DecUsage();
uint64 ClientId(uint64 prevId = 0, bool forceNew = false);
inline uint32 LeaseTime();
virtual status_t ProcessCallback(RPC::CallbackRequest* request,
Connection* connection);
status_t CallbackRecall(RequestInterpreter* request,
ReplyBuilder* reply);
status_t RecallAll();
status_t CallbackGetAttr(RequestInterpreter* request,
ReplyBuilder* reply);
private:
status_t _GetLeaseTime();
status_t _StartRenewing();
status_t _Renewal();
static status_t _RenewalThreadStart(void* ptr);
thread_id fThread;
bool fThreadCancel;
sem_id fWaitCancel;
mutex fThreadStartLock;
uint32 fLeaseTime;
uint64 fClientId;
bool fClientIdInit;
time_t fClientIdLastUse;
mutex fClientIdLock;
uint32 fUseCount;
DoublyLinkedList<FileSystem> fFileSystems;
mutex fFSLock;
RPC::Server* fServer;
};
inline void
NFS4Server::IncUsage()
{
MutexLocker _(fFSLock);
fUseCount++;
_StartRenewing();
fClientIdLastUse = time(NULL);
}
inline void
NFS4Server::DecUsage()
{
MutexLocker _(fFSLock);
fClientIdLastUse = time(NULL);
fUseCount--;
}
inline uint32
NFS4Server::LeaseTime()
{
if (fLeaseTime == 0)
_GetLeaseTime();
return fLeaseTime;
}
#endif // NFS4SERVER_H
@@ -0,0 +1,322 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "OpenState.h"
#include <util/AutoLock.h>
#include "FileSystem.h"
#include "Request.h"
#include "WorkQueue.h"
OpenState::OpenState()
:
fOpened(false),
fDelegation(NULL),
fLocks(NULL),
fLockOwners(NULL)
{
mutex_init(&fLock, NULL);
mutex_init(&fLocksLock, NULL);
mutex_init(&fOwnerLock, NULL);
}
OpenState::~OpenState()
{
if (fOpened)
fFileSystem->RemoveOpenFile(this);
Close();
mutex_destroy(&fLock);
mutex_destroy(&fLocksLock);
mutex_destroy(&fOwnerLock);
}
LockOwner*
OpenState::GetLockOwner(uint32 owner)
{
LockOwner* current = fLockOwners;
while (current != NULL) {
if (current->fOwner == owner)
return current;
current = current->fNext;
}
current = new LockOwner(owner);
if (current == NULL)
return NULL;
current->fNext = fLockOwners;
if (fLockOwners != NULL)
fLockOwners->fPrev = current;
fLockOwners = current;
return current;
}
// Caller must hold fLocksLock
void
OpenState::AddLock(LockInfo* lock)
{
lock->fNext = fLocks;
fLocks = lock;
}
// Caller must hold fLocksLock
void
OpenState::RemoveLock(LockInfo* lock, LockInfo* prev)
{
if (prev != NULL)
prev->fNext = lock->fNext;
else
fLocks = lock->fNext;
}
void
OpenState::DeleteLock(LockInfo* lock)
{
MutexLocker _(fOwnerLock);
LockOwner* owner = lock->fOwner;
delete lock;
if (owner->fUseCount == 0) {
if (owner->fPrev)
owner->fPrev->fNext = owner->fNext;
else
fLockOwners = owner->fNext;
if (owner->fNext)
owner->fNext->fPrev = owner->fPrev;
_ReleaseLockOwner(owner);
delete owner;
}
}
status_t
OpenState::_ReleaseLockOwner(LockOwner* owner)
{
ASSERT(owner != NULL);
do {
RPC::Server* server = fFileSystem->Server();
Request request(server, fFileSystem);
RequestBuilder& req = request.Builder();
req.ReleaseLockOwner(this, owner);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(reply.NFS4Error(), server))
continue;
return reply.ReleaseLockOwner();
} while (true);
}
status_t
OpenState::Reclaim(uint64 newClientID)
{
if (!fOpened)
return B_OK;
MutexLocker _(fLock);
if (fClientID == newClientID)
return B_OK;
fClientID = newClientID;
_ReclaimOpen(newClientID);
_ReclaimLocks(newClientID);
return B_OK;
}
status_t
OpenState::_ReclaimOpen(uint64 newClientID)
{
bool confirm;
OpenDelegationData delegation;
delegation.fType = OPEN_DELEGATE_NONE;
delegation.fRecall = false;
status_t result;
uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
OpenDelegation delegType = fDelegation != NULL ? fDelegation->Type()
: OPEN_DELEGATE_NONE;
do {
RPC::Server* server = fFileSystem->Server();
Request request(server, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.Open(CLAIM_PREVIOUS, sequence, sModeToAccess(fMode), newClientID,
OPEN4_NOCREATE, fFileSystem->OpenOwner(), NULL, NULL, 0, false,
delegType);
result = request.Send();
if (result != B_OK) {
fFileSystem->OpenOwnerSequenceUnlock(sequence);
return result;
}
ReplyInterpreter& reply = request.Reply();
sequence += IncrementSequence(reply.NFS4Error());
if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
&& HandleErrors(reply.NFS4Error(), server, NULL, NULL, &sequence)) {
continue;
}
reply.PutFH();
result = reply.Open(fStateID, &fStateSeq, &confirm, &delegation);
if (result != B_OK) {
fFileSystem->OpenOwnerSequenceUnlock(sequence);
return result;
}
break;
} while (true);
if (fDelegation != NULL)
fDelegation->SetData(delegation);
if (delegation.fRecall) {
DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
args->fDelegation = fDelegation;
args->fTruncate = false;
gWorkQueue->EnqueueJob(DelegationRecall, args);
}
if (confirm)
result = ConfirmOpen(fInfo.fHandle, this, &sequence);
fFileSystem->OpenOwnerSequenceUnlock(sequence);
return result;
}
status_t
OpenState::_ReclaimLocks(uint64 newClientID)
{
MutexLocker _(fLocksLock);
LockInfo* linfo = fLocks;
while (linfo != NULL) {
MutexLocker locker(linfo->fOwner->fLock);
if (linfo->fOwner->fClientId != newClientID) {
memset(linfo->fOwner->fStateId, 0, sizeof(linfo->fOwner->fStateId));
linfo->fOwner->fClientId = newClientID;
}
uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
do {
RPC::Server* server = fFileSystem->Server();
Request request(server, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.Lock(this, linfo, &sequence, true);
status_t result = request.Send();
if (result != B_OK) {
fFileSystem->OpenOwnerSequenceUnlock(sequence);
break;
}
ReplyInterpreter& reply = request.Reply();
sequence += IncrementSequence(reply.NFS4Error());
if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
&& reply.NFS4Error() != NFS4ERR_STALE_STATEID
&& HandleErrors(reply.NFS4Error(), server, NULL, NULL,
&sequence)) {
continue;
}
reply.PutFH();
reply.Lock(linfo);
fFileSystem->OpenOwnerSequenceUnlock(sequence);
break;
} while (true);
locker.Unlock();
linfo = linfo->fNext;
}
return B_OK;
}
status_t
OpenState::Close()
{
if (!fOpened)
return B_OK;
MutexLocker _(fLock);
fOpened = false;
uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
do {
RPC::Server* serv = fFileSystem->Server();
Request request(serv, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.Close(sequence, fStateID, fStateSeq);
status_t result = request.Send();
if (result != B_OK) {
fFileSystem->OpenOwnerSequenceUnlock(sequence);
return result;
}
ReplyInterpreter& reply = request.Reply();
sequence += IncrementSequence(reply.NFS4Error());
// RFC 3530 8.10.1. Some servers does not do anything to help client
// recognize retried CLOSE requests so we just assume that BAD_STATEID
// on CLOSE request is just a result of retransmission.
if (reply.NFS4Error() == NFS4ERR_BAD_STATEID) {
fFileSystem->OpenOwnerSequenceUnlock(sequence);
return B_OK;
}
if (HandleErrors(reply.NFS4Error(), serv, NULL, this, &sequence))
continue;
fFileSystem->OpenOwnerSequenceUnlock(sequence);
reply.PutFH();
return reply.Close();
} while (true);
}
@@ -0,0 +1,60 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef OPENSTATE_H
#define OPENSTATE_H
#include <lock.h>
#include <SupportDefs.h>
#include <util/KernelReferenceable.h>
#include "Cookie.h"
#include "NFS4Object.h"
struct OpenState : public NFS4Object, public KernelReferenceable,
public DoublyLinkedListLinkImpl<OpenState> {
OpenState();
~OpenState();
uint64 fClientID;
int fMode;
mutex fLock;
uint32 fStateID[3];
uint32 fStateSeq;
bool fOpened;
Delegation* fDelegation;
LockInfo* fLocks;
mutex fLocksLock;
LockOwner* fLockOwners;
mutex fOwnerLock;
LockOwner* GetLockOwner(uint32 owner);
void AddLock(LockInfo* lock);
void RemoveLock(LockInfo* lock, LockInfo* prev);
void DeleteLock(LockInfo* lock);
status_t Reclaim(uint64 newClientID);
status_t Close();
private:
status_t _ReclaimOpen(uint64 newClientID);
status_t _ReclaimLocks(uint64 newClientID);
status_t _ReleaseLockOwner(LockOwner* owner);
};
#endif // OPENSTATE_H
@@ -0,0 +1,91 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCAuth.h"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <SupportDefs.h>
#include <util/kernel_cpp.h>
#include "RPCDefs.h"
using namespace RPC;
Auth::Auth()
{
}
const Auth*
Auth::CreateNone()
{
Auth* auth = new(std::nothrow) Auth;
if (auth == NULL)
return NULL;
auth->fStream.AddInt(AUTH_NONE);
auth->fStream.AddOpaque(NULL, 0);
if (auth->fStream.Error() != B_OK) {
delete auth;
return NULL;
}
return auth;
}
const Auth*
Auth::CreateSys()
{
Auth* auth = new(std::nothrow) Auth;
if (auth == NULL)
return NULL;
XDR::WriteStream xdr;
xdr.AddUInt(time(NULL));
char hostname[255];
if (gethostname(hostname, 255) < 0)
strcpy(hostname, "unknown");
xdr.AddString(hostname, 255);
xdr.AddUInt(getuid());
xdr.AddUInt(getgid());
int count = getgroups(0, NULL);
gid_t* groups = (gid_t*)malloc(count * sizeof(gid_t));
int len = getgroups(count, groups);
if (len > 0) {
len = min_c(len, 16);
xdr.AddUInt(len);
for (int i = 0; i < len; i++)
xdr.AddUInt((uint32)groups[i]);
} else
xdr.AddUInt(0);
free(groups);
if (xdr.Error() != B_OK) {
delete auth;
return NULL;
}
auth->fStream.AddInt(AUTH_SYS);
auth->fStream.AddOpaque(xdr);
if (auth->fStream.Error() != B_OK) {
delete auth;
return NULL;
}
return auth;
}
@@ -0,0 +1,41 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCAUTH_H
#define RPCAUTH_H
#include "XDR.h"
namespace RPC {
class Auth {
public:
inline const XDR::WriteStream& Stream() const;
static const Auth* CreateNone();
static const Auth* CreateSys();
private:
Auth();
XDR::WriteStream fStream;
};
inline const XDR::WriteStream&
Auth::Stream() const
{
return fStream;
}
} // namespace RPC
#endif // RPCAUTH_H
@@ -0,0 +1,71 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCCall.h"
#include <debug.h>
#include <util/kernel_cpp.h>
#include "RPCDefs.h"
using namespace RPC;
Call::Call()
{
}
Call*
Call::Create(uint32 proc, const Auth* creds, const Auth* ver)
{
ASSERT(creds != NULL);
ASSERT(ver != NULL);
Call* call = new(std::nothrow) Call;
if (call == NULL)
return NULL;
// XID will be determined and set by RPC::Server
call->fXIDPosition = call->fStream.Current();
call->fStream.AddUInt(0);
call->fStream.AddInt(CALL);
call->fStream.AddUInt(VERSION);
call->fStream.AddUInt(PROGRAM_NFS);
call->fStream.AddUInt(NFS_VERSION);
call->fStream.AddUInt(proc);
call->fStream.Append(creds->Stream());
delete creds;
call->fStream.Append(ver->Stream());
delete ver;
if (call->fStream.Error() != B_OK) {
delete call;
return NULL;
}
return call;
}
Call::~Call()
{
}
void
Call::SetXID(uint32 xid)
{
fStream.InsertUInt(fXIDPosition, xid);
}
@@ -0,0 +1,46 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCCALL_H
#define RPCCALL_H
#include "RPCAuth.h"
#include "XDR.h"
namespace RPC {
class Call {
public:
static Call* Create(uint32 proc, const Auth* creds,
const Auth* ver);
~Call();
void SetXID(uint32 x);
inline XDR::WriteStream& Stream();
private:
Call();
XDR::Stream::Position fXIDPosition;
XDR::WriteStream fStream;
};
inline XDR::WriteStream&
Call::Stream()
{
return fStream;
}
} // namespace RPC
#endif // RPCCALL_H
@@ -0,0 +1,33 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCCallback.h"
#include "RPCCallbackRequest.h"
#include "RPCServer.h"
using namespace RPC;
Callback::Callback(Server* server)
:
fServer(server)
{
}
status_t
Callback::EnqueueRequest(CallbackRequest* request, Connection* connection)
{
ASSERT(request != NULL);
ASSERT(connection != NULL);
return fServer->PrivateData()->ProcessCallback(request, connection);
}
@@ -0,0 +1,73 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCCALLBACK_H
#define RPCCALLBACK_H
#include "Connection.h"
namespace RPC {
class CallbackServer;
class CallbackRequest;
class Server;
class Callback {
public:
Callback(Server* server);
inline void SetID(int32 id);
inline int32 ID();
inline void SetCBServer(CallbackServer* server);
inline CallbackServer* CBServer();
status_t EnqueueRequest(CallbackRequest* request,
Connection* connection);
private:
CallbackServer* fCBServer;
Server* fServer;
int32 fID;
};
inline void
Callback::SetID(int32 id)
{
fID = id;
}
inline int32
Callback::ID()
{
return fID;
}
inline void
Callback::SetCBServer(CallbackServer* server)
{
fCBServer = server;
}
inline CallbackServer*
Callback::CBServer()
{
return fCBServer;
}
} // namespace RPC
#endif // RPCCALLBACK_H
@@ -0,0 +1,49 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCCallbackReply.h"
#include <util/kernel_cpp.h>
#include "RPCDefs.h"
using namespace RPC;
CallbackReply::CallbackReply()
{
}
CallbackReply*
CallbackReply::Create(uint32 xid, AcceptStat rpcError)
{
CallbackReply* reply = new(std::nothrow) CallbackReply;
if (reply == NULL)
return NULL;
reply->fStream.AddUInt(xid);
reply->fStream.AddInt(REPLY);
reply->fStream.AddUInt(MSG_ACCEPTED);
reply->fStream.AddInt(AUTH_NONE);
reply->fStream.AddOpaque(NULL, 0);
reply->fStream.AddUInt(rpcError);
return reply;
}
CallbackReply::~CallbackReply()
{
}
@@ -0,0 +1,42 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCCALLBACKREPLY_H
#define RPCCALLBACKREPLY_H
#include "RPCDefs.h"
#include "XDR.h"
namespace RPC {
class CallbackReply {
public:
static CallbackReply* Create(uint32 xid,
AcceptStat rpcError = SUCCESS);
~CallbackReply();
inline XDR::WriteStream& Stream();
private:
CallbackReply();
XDR::WriteStream fStream;
};
inline XDR::WriteStream&
CallbackReply::Stream()
{
return fStream;
}
} // namespace RPC
#endif // RPCCALLBACKREPLY_H
@@ -0,0 +1,80 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCCallbackRequest.h"
#include <stdlib.h>
#include <debug.h>
#include "NFS4Defs.h"
#include "RPCDefs.h"
using namespace RPC;
CallbackRequest::CallbackRequest(void* buffer, int size)
:
fError(B_BAD_VALUE),
fRPCError(GARBAGE_ARGS),
fStream(buffer, size),
fBuffer(buffer)
{
ASSERT(buffer != NULL);
fXID = fStream.GetUInt();
if (fStream.GetUInt() != CALL)
return;
if (fStream.GetUInt() != VERSION)
return;
if (fStream.GetUInt() != PROGRAM_NFS_CB) {
fRPCError = PROG_UNAVAIL;
return;
}
if (fStream.GetUInt() != NFS_CB_VERSION) {
fRPCError = PROG_MISMATCH;
return;
}
fProcedure = fStream.GetUInt();
fStream.GetUInt();
fStream.GetOpaque(NULL);
fStream.GetUInt();
fStream.GetOpaque(NULL);
if (fProcedure == CallbackProcCompound) {
fStream.GetOpaque(NULL); // TODO: tag may be important
if (fStream.GetUInt() != 0)
return;
fID = fStream.GetUInt();
fRPCError = SUCCESS;
fError = B_OK;
} else if (fProcedure == CallbackProcNull) {
fRPCError = SUCCESS;
fError = B_OK;
} else
fRPCError = PROC_UNAVAIL;
}
CallbackRequest::~CallbackRequest()
{
free(fBuffer);
}
@@ -0,0 +1,93 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCCALLBACKREQUEST_H
#define RPCCALLBACKREQUEST_H
#include "RPCDefs.h"
#include "XDR.h"
namespace RPC {
class CallbackRequest {
public:
CallbackRequest(void* buffer, int size);
~CallbackRequest();
inline uint32 XID();
inline uint32 ID();
inline uint32 Procedure();
inline status_t Error();
inline AcceptStat RPCError();
inline XDR::ReadStream& Stream();
private:
uint32 fXID;
uint32 fID;
uint32 fProcedure;
status_t fError;
AcceptStat fRPCError;
XDR::ReadStream fStream;
void* fBuffer;
};
inline uint32
CallbackRequest::XID()
{
return fXID;
}
inline uint32
CallbackRequest::ID()
{
return fID;
}
inline uint32
CallbackRequest::Procedure()
{
return fProcedure;
}
inline status_t
CallbackRequest::Error()
{
return fError;
}
inline AcceptStat
CallbackRequest::RPCError()
{
return fRPCError;
}
inline XDR::ReadStream&
CallbackRequest::Stream()
{
return fStream;
}
} // namespace RPC
#endif // RPCCALLBACKREQUEST_H
@@ -0,0 +1,374 @@
/*
* Copyright 2012-2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCCallbackServer.h"
#include "NFS4Defs.h"
#include "RPCCallback.h"
#include "RPCCallbackReply.h"
#include "RPCCallbackRequest.h"
#include "RPCServer.h"
using namespace RPC;
CallbackServer* gRPCCallbackServer = NULL;
CallbackServer* gRPCCallbackServer6 = NULL;
CallbackServer::CallbackServer(int networkFamily)
:
fConnectionList(NULL),
fListener(NULL),
fThreadRunning(false),
fCallbackArray(NULL),
fArraySize(0),
fFreeSlot(-1),
fNetworkFamily(networkFamily)
{
mutex_init(&fConnectionLock, NULL);
mutex_init(&fThreadLock, NULL);
rw_lock_init(&fArrayLock, NULL);
}
CallbackServer::~CallbackServer()
{
StopServer();
free(fCallbackArray);
rw_lock_destroy(&fArrayLock);
mutex_destroy(&fThreadLock);
mutex_destroy(&fConnectionLock);
}
CallbackServer*
CallbackServer::Get(Server* server)
{
ASSERT(server != NULL);
int family = server->ID().Family();
ASSERT(family == AF_INET || family == AF_INET6);
int idx;
switch (family) {
case AF_INET:
idx = 0;
break;
case AF_INET6:
idx = 1;
break;
default:
return NULL;
}
MutexLocker _(fServerCreationLock);
if (fServers[idx] == NULL)
fServers[idx] = new CallbackServer(family);
return fServers[idx];
}
void
CallbackServer::ShutdownAll()
{
MutexLocker _(fServerCreationLock);
for (unsigned int i = 0; i < sizeof(fServers) / sizeof(fServers[0]); i++)
delete fServers[i];
memset(&fServers, 0, sizeof(fServers));
}
mutex CallbackServer::fServerCreationLock = MUTEX_INITIALIZER(NULL);
CallbackServer* CallbackServer::fServers[2] = { NULL, NULL };
status_t
CallbackServer::RegisterCallback(Callback* callback)
{
ASSERT(callback != NULL);
status_t result = StartServer();
if (result != B_OK)
return result;
WriteLocker _(fArrayLock);
if (fFreeSlot == -1) {
uint32 newSize = max_c(fArraySize * 2, 4);
uint32 size = newSize * sizeof(CallbackSlot);
CallbackSlot* array = reinterpret_cast<CallbackSlot*>(malloc(size));
if (array == NULL)
return B_NO_MEMORY;
if (fCallbackArray != NULL)
memcpy(array, fCallbackArray, fArraySize * sizeof(CallbackSlot));
for (uint32 i = fArraySize; i < newSize; i++)
array[i].fNext = i + 1;
array[newSize - 1].fNext = -1;
fCallbackArray = array;
fFreeSlot = fArraySize;
fArraySize = newSize;
}
int32 id = fFreeSlot;
fFreeSlot = fCallbackArray[id].fNext;
fCallbackArray[id].fCallback = callback;
callback->SetID(id);
callback->SetCBServer(this);
return B_OK;
}
status_t
CallbackServer::UnregisterCallback(Callback* callback)
{
ASSERT(callback != NULL);
ASSERT(callback->CBServer() == this);
int32 id = callback->ID();
WriteLocker _(fArrayLock);
fCallbackArray[id].fNext = fFreeSlot;
fFreeSlot = id;
callback->SetCBServer(NULL);
return B_OK;
}
status_t
CallbackServer::StartServer()
{
MutexLocker _(fThreadLock);
if (fThreadRunning)
return B_OK;
status_t result = ConnectionListener::Listen(&fListener, fNetworkFamily);
if (result != B_OK)
return result;
fThread = spawn_kernel_thread(&CallbackServer::ListenerThreadLauncher,
"NFSv4 Callback Listener", B_NORMAL_PRIORITY, this);
if (fThread < B_OK)
return fThread;
fThreadRunning = true;
result = resume_thread(fThread);
if (result != B_OK) {
kill_thread(fThread);
fThreadRunning = false;
return result;
}
return B_OK;
}
status_t
CallbackServer::StopServer()
{
MutexLocker _(&fThreadLock);
if (!fThreadRunning)
return B_OK;
fListener->Disconnect();
status_t result;
wait_for_thread(fThread, &result);
MutexLocker locker(fConnectionLock);
while (fConnectionList != NULL) {
ConnectionEntry* entry = fConnectionList;
fConnectionList = entry->fNext;
entry->fConnection->Disconnect();
status_t result;
wait_for_thread(entry->fThread, &result);
delete entry->fConnection;
delete entry;
}
delete fListener;
fThreadRunning = false;
return B_OK;
}
status_t
CallbackServer::NewConnection(Connection* connection)
{
ASSERT(connection != NULL);
ConnectionEntry* entry = new ConnectionEntry;
entry->fConnection = connection;
entry->fPrev = NULL;
MutexLocker locker(fConnectionLock);
entry->fNext = fConnectionList;
if (fConnectionList != NULL)
fConnectionList->fPrev = entry;
fConnectionList = entry;
locker.Unlock();
void** arguments = reinterpret_cast<void**>(malloc(sizeof(void*) * 2));
if (arguments == NULL)
return B_NO_MEMORY;
arguments[0] = this;
arguments[1] = entry;
thread_id thread;
thread = spawn_kernel_thread(&CallbackServer::ConnectionThreadLauncher,
"NFSv4 Callback Connection", B_NORMAL_PRIORITY, arguments);
if (thread < B_OK) {
ReleaseConnection(entry);
free(arguments);
return thread;
}
entry->fThread = thread;
status_t result = resume_thread(thread);
if (result != B_OK) {
kill_thread(thread);
ReleaseConnection(entry);
free(arguments);
return result;
}
return B_OK;
}
status_t
CallbackServer::ReleaseConnection(ConnectionEntry* entry)
{
ASSERT(entry != NULL);
MutexLocker _(fConnectionLock);
if (entry->fNext != NULL)
entry->fNext->fPrev = entry->fPrev;
if (entry->fPrev != NULL)
entry->fPrev->fNext = entry->fNext;
else
fConnectionList = entry->fNext;
delete entry->fConnection;
delete entry;
return B_OK;
}
status_t
CallbackServer::ConnectionThreadLauncher(void* object)
{
ASSERT(object != NULL);
void** objects = reinterpret_cast<void**>(object);
CallbackServer* server = reinterpret_cast<CallbackServer*>(objects[0]);
ConnectionEntry* entry = reinterpret_cast<ConnectionEntry*>(objects[1]);
free(objects);
return server->ConnectionThread(entry);
}
status_t
CallbackServer::ConnectionThread(ConnectionEntry* entry)
{
ASSERT(entry != NULL);
Connection* connection = entry->fConnection;
CallbackReply* reply;
while (fThreadRunning) {
uint32 size;
void* buffer;
status_t result = connection->Receive(&buffer, &size);
if (result != B_OK) {
if (result != ECONNABORTED)
ReleaseConnection(entry);
return result;
}
CallbackRequest* request = new CallbackRequest(buffer, size);
if (request == NULL || request->Error() != B_OK) {
free(buffer);
continue;
} else if (request != NULL && request->Error() != B_OK) {
reply = CallbackReply::Create(request->XID(), request->RPCError());
if (reply != NULL) {
connection->Send(reply->Stream().Buffer(),
reply->Stream().Size());
delete reply;
}
free(buffer);
continue;
}
switch (request->Procedure()) {
case CallbackProcCompound:
GetCallback(request->ID())->EnqueueRequest(request, connection);
break;
case CallbackProcNull:
reply = CallbackReply::Create(request->XID());
if (reply != NULL) {
connection->Send(reply->Stream().Buffer(),
reply->Stream().Size());
delete reply;
}
default:
free(buffer);
}
}
return B_OK;
}
status_t
CallbackServer::ListenerThreadLauncher(void* object)
{
ASSERT(object != NULL);
CallbackServer* server = reinterpret_cast<CallbackServer*>(object);
return server->ListenerThread();
}
status_t
CallbackServer::ListenerThread()
{
while (fThreadRunning) {
Connection* connection;
status_t result = fListener->AcceptConnection(&connection);
if (result != B_OK) {
fThreadRunning = false;
return result;
}
result = NewConnection(connection);
if (result != B_OK)
delete connection;
}
return B_OK;
}
@@ -0,0 +1,108 @@
/*
* Copyright 2012-2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCCALLBACKSERVER_H
#define RPCCALLBACKSERVER_H
#include <util/AutoLock.h>
#include "Connection.h"
namespace RPC {
class Callback;
class Server;
struct ConnectionEntry {
Connection* fConnection;
thread_id fThread;
ConnectionEntry* fNext;
ConnectionEntry* fPrev;
};
union CallbackSlot {
Callback* fCallback;
int32 fNext;
};
class CallbackServer {
public:
CallbackServer(int networkFamily);
~CallbackServer();
static CallbackServer* Get(Server* server);
static void ShutdownAll();
status_t RegisterCallback(Callback* callback);
status_t UnregisterCallback(Callback* callback);
inline PeerAddress LocalID();
protected:
status_t StartServer();
status_t StopServer();
status_t NewConnection(Connection* connection);
status_t ReleaseConnection(ConnectionEntry* entry);
static status_t ListenerThreadLauncher(void* object);
status_t ListenerThread();
static status_t ConnectionThreadLauncher(void* object);
status_t ConnectionThread(ConnectionEntry* entry);
inline Callback* GetCallback(int32 id);
private:
static mutex fServerCreationLock;
static CallbackServer* fServers[2];
mutex fConnectionLock;
ConnectionEntry* fConnectionList;
ConnectionListener* fListener;
mutex fThreadLock;
thread_id fThread;
bool fThreadRunning;
rw_lock fArrayLock;
CallbackSlot* fCallbackArray;
uint32 fArraySize;
int32 fFreeSlot;
int fNetworkFamily;
};
inline PeerAddress
CallbackServer::LocalID()
{
PeerAddress address;
ASSERT(fListener != NULL);
fListener->GetLocalAddress(&address);
return address;
}
inline Callback*
CallbackServer::GetCallback(int32 id)
{
ReadLocker _(fArrayLock);
if (id >= 0 && static_cast<uint32>(id) < fArraySize)
return fCallbackArray[id].fCallback;
return NULL;
}
} // namespace RPC
#endif // RPCCALLBACKSERVER_H
@@ -0,0 +1,61 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCDEFS_H
#define RPCDEFS_H
namespace RPC {
enum {
VERSION = 2
};
enum {
PROGRAM_NFS = 100003,
PROGRAM_NFS_CB = 0x40000000
};
enum {
NFS_VERSION = 4,
NFS_CB_VERSION = 1
};
enum {
CALL = 0,
REPLY = 1
};
enum {
MSG_ACCEPTED = 0,
MSG_DENIED = 1
};
enum AcceptStat {
SUCCESS = 0, /* RPC executed successfully */
PROG_UNAVAIL = 1, /* remote hasn't exported program */
PROG_MISMATCH = 2, /* remote can't support version # */
PROC_UNAVAIL = 3, /* program can't support procedure */
GARBAGE_ARGS = 4, /* procedure can't decode params */
SYSTEM_ERR = 5 /* e.g. memory allocation failure */
};
enum RejectStat {
RPC_MISMATCH = 0, /* RPC version number != 2 */
AUTH_ERROR = 1 /* remote can't authenticate caller */
};
enum AuthFlavour {
AUTH_NONE = 0,
AUTH_SYS = 1
};
} // namespace RPC
#endif // RPCDEFS_H
@@ -0,0 +1,73 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCReply.h"
#include <debug.h>
#include <util/kernel_cpp.h>
#include "RPCDefs.h"
using namespace RPC;
Reply::Reply(void* buffer, int size)
:
fError(B_OK),
fStream(buffer, size),
fBuffer(buffer)
{
ASSERT(buffer != NULL);
fXID = fStream.GetUInt();
if (fStream.GetInt() != REPLY) {
fError = B_BAD_VALUE;
return;
}
if (fStream.GetInt() == MSG_ACCEPTED) {
fStream.GetInt();
fStream.GetOpaque(NULL);
switch (fStream.GetInt()) {
case SUCCESS:
return;
case PROG_UNAVAIL:
case PROG_MISMATCH:
case PROC_UNAVAIL:
fError = B_DEVICE_NOT_FOUND;
return;
case GARBAGE_ARGS:
fError = B_MISMATCHED_VALUES;
return;
case SYSTEM_ERR:
fError = B_ERROR;
return;
default:
fError = B_BAD_VALUE;
return;
}
} else { // MSG_DENIED
if (fStream.GetInt() == RPC_MISMATCH) {
fError = B_DEVICE_NOT_FOUND;
return;
} else { // AUTH_ERROR
fError = B_PERMISSION_DENIED;
return;
}
}
}
Reply::~Reply()
{
free(fBuffer);
}
@@ -0,0 +1,60 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCREPLY_H
#define RPCREPLY_H
#include "XDR.h"
namespace RPC {
class Reply {
public:
Reply(void* buffer, int size);
~Reply();
inline uint32 GetXID();
inline status_t Error();
inline XDR::ReadStream& Stream();
private:
uint32 fXID;
status_t fError;
XDR::ReadStream fStream;
void* fBuffer;
};
inline uint32
Reply::GetXID()
{
return fXID;
}
inline status_t
Reply::Error()
{
return fError;
}
inline XDR::ReadStream&
Reply::Stream()
{
return fStream;
}
} // namespace RPC
#endif // RPCREPLY_H
@@ -0,0 +1,529 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RPCServer.h"
#include <stdlib.h>
#include <util/AutoLock.h>
#include "RPCCallbackServer.h"
#include "RPCReply.h"
using namespace RPC;
RequestManager::RequestManager()
:
fQueueHead(NULL),
fQueueTail(NULL)
{
mutex_init(&fLock, NULL);
}
RequestManager::~RequestManager()
{
mutex_destroy(&fLock);
}
void
RequestManager::AddRequest(Request* request)
{
ASSERT(request != NULL);
MutexLocker _(fLock);
if (fQueueTail != NULL)
fQueueTail->fNext = request;
else
fQueueHead = request;
fQueueTail = request;
request->fNext = NULL;
}
Request*
RequestManager::FindRequest(uint32 xid)
{
MutexLocker _(fLock);
Request* req = fQueueHead;
Request* prev = NULL;
while (req != NULL) {
if (req->fXID == xid) {
if (prev != NULL)
prev->fNext = req->fNext;
if (fQueueTail == req)
fQueueTail = prev;
if (fQueueHead == req)
fQueueHead = req->fNext;
return req;
}
prev = req;
req = req->fNext;
}
return NULL;
}
Server::Server(Connection* connection, PeerAddress* address)
:
fConnection(connection),
fAddress(address),
fPrivateData(NULL),
fCallback(NULL),
fRepairCount(0),
fXID(rand() << 1)
{
ASSERT(connection != NULL);
ASSERT(address != NULL);
mutex_init(&fCallbackLock, NULL);
mutex_init(&fRepairLock, NULL);
_StartListening();
}
Server::~Server()
{
if (fCallback != NULL)
fCallback->CBServer()->UnregisterCallback(fCallback);
delete fCallback;
mutex_destroy(&fCallbackLock);
mutex_destroy(&fRepairLock);
delete fPrivateData;
fThreadCancel = true;
fConnection->Disconnect();
status_t result;
wait_for_thread(fThread, &result);
delete fConnection;
}
status_t
Server::_StartListening()
{
fThreadCancel = false;
fThreadError = B_OK;
fThread = spawn_kernel_thread(&Server::_ListenerThreadStart,
"NFSv4 Listener", B_NORMAL_PRIORITY, this);
if (fThread < B_OK)
return fThread;
status_t result = resume_thread(fThread);
if (result != B_OK) {
kill_thread(fThread);
return result;
}
return B_OK;
}
status_t
Server::SendCallAsync(Call* call, Reply** reply, Request** request)
{
ASSERT(call != NULL);
ASSERT(reply != NULL);
ASSERT(request != NULL);
if (fThreadError != B_OK && Repair() != B_OK)
return fThreadError;
Request* req = new(std::nothrow) Request;
if (req == NULL)
return B_NO_MEMORY;
uint32 xid = _GetXID();
call->SetXID(xid);
req->fXID = xid;
req->fReply = reply;
req->fEvent.Init(&req->fEvent, NULL);
req->fDone = false;
req->fError = B_OK;
req->fNext = NULL;
fRequests.AddRequest(req);
*request = req;
return ResendCallAsync(call, req);
}
status_t
Server::ResendCallAsync(Call* call, Request* request)
{
ASSERT(call != NULL);
ASSERT(request != NULL);
if (fThreadError != B_OK && Repair() != B_OK) {
fRequests.FindRequest(request->fXID);
delete request;
return fThreadError;
}
XDR::WriteStream& stream = call->Stream();
status_t result = fConnection->Send(stream.Buffer(), stream.Size());
if (result != B_OK) {
fRequests.FindRequest(request->fXID);
delete request;
return result;
}
return B_OK;
}
status_t
Server::WakeCall(Request* request)
{
ASSERT(request != NULL);
Request* req = fRequests.FindRequest(request->fXID);
if (req == NULL)
return B_OK;
request->fError = B_FILE_ERROR;
*request->fReply = NULL;
request->fDone = true;
request->fEvent.NotifyAll();
return B_OK;
}
status_t
Server::Repair()
{
uint32 thisRepair = fRepairCount;
MutexLocker _(fRepairLock);
if (fRepairCount != thisRepair)
return B_OK;
fThreadCancel = true;
status_t result = fConnection->Reconnect();
if (result != B_OK)
return result;
wait_for_thread(fThread, &result);
result = _StartListening();
if (result == B_OK)
fRepairCount++;
return result;
}
Callback*
Server::GetCallback()
{
MutexLocker _(fCallbackLock);
if (fCallback == NULL) {
fCallback = new(std::nothrow) Callback(this);
if (fCallback == NULL)
return NULL;
CallbackServer* server = CallbackServer::Get(this);
if (server == NULL) {
delete fCallback;
return NULL;
}
if (server->RegisterCallback(fCallback) != B_OK) {
delete fCallback;
return NULL;
}
}
return fCallback;
}
uint32
Server::_GetXID()
{
return static_cast<uint32>(atomic_add(&fXID, 1));
}
status_t
Server::_Listener()
{
status_t result;
uint32 size;
void* buffer = NULL;
while (!fThreadCancel) {
result = fConnection->Receive(&buffer, &size);
if (result == B_NO_MEMORY)
continue;
else if (result != B_OK) {
fThreadError = result;
return result;
}
ASSERT(buffer != NULL && size > 0);
Reply* reply = new(std::nothrow) Reply(buffer, size);
if (reply == NULL) {
free(buffer);
continue;
}
Request* req = fRequests.FindRequest(reply->GetXID());
if (req != NULL) {
*req->fReply = reply;
req->fDone = true;
req->fEvent.NotifyAll();
} else
delete reply;
}
return B_OK;
}
status_t
Server::_ListenerThreadStart(void* object)
{
ASSERT(object != NULL);
Server* server = reinterpret_cast<Server*>(object);
return server->_Listener();
}
ServerManager::ServerManager()
:
fRoot(NULL)
{
mutex_init(&fLock, NULL);
}
ServerManager::~ServerManager()
{
mutex_destroy(&fLock);
}
status_t
ServerManager::Acquire(Server** _server, AddressResolver* resolver,
ProgramData* (*createPrivateData)(Server*))
{
PeerAddress address;
status_t result;
while ((result = resolver->GetNextAddress(&address)) == B_OK) {
result = _Acquire(_server, address, createPrivateData);
if (result == B_OK)
break;
}
return result;
}
status_t
ServerManager::_Acquire(Server** _server, const PeerAddress& address,
ProgramData* (*createPrivateData)(Server*))
{
ASSERT(_server != NULL);
ASSERT(createPrivateData != NULL);
status_t result;
MutexLocker locker(fLock);
ServerNode* node = _Find(address);
if (node != NULL) {
node->fRefCount++;
*_server = node->fServer;
return B_OK;
}
node = new(std::nothrow) ServerNode;
if (node == NULL)
return B_NO_MEMORY;
node->fID = address;
Connection* conn;
result = Connection::Connect(&conn, address);
if (result != B_OK) {
delete node;
return result;
}
node->fServer = new Server(conn, &node->fID);
if (node->fServer == NULL) {
delete node;
delete conn;
return B_NO_MEMORY;
}
node->fServer->SetPrivateData(createPrivateData(node->fServer));
node->fRefCount = 1;
node->fLeft = node->fRight = NULL;
ServerNode* nd = _Insert(node);
if (nd != node) {
nd->fRefCount++;
delete node->fServer;
delete node;
*_server = nd->fServer;
return B_OK;
}
*_server = node->fServer;
return B_OK;
}
void
ServerManager::Release(Server* server)
{
ASSERT(server != NULL);
MutexLocker _(fLock);
ServerNode* node = _Find(server->ID());
if (node != NULL) {
node->fRefCount--;
if (node->fRefCount == 0) {
_Delete(node);
delete node->fServer;
delete node;
}
}
}
ServerNode*
ServerManager::_Find(const PeerAddress& address)
{
ServerNode* node = fRoot;
while (node != NULL) {
if (node->fID == address)
return node;
if (node->fID < address)
node = node->fRight;
else
node = node->fLeft;
}
return node;
}
void
ServerManager::_Delete(ServerNode* node)
{
ASSERT(node != NULL);
bool found = false;
ServerNode* previous = NULL;
ServerNode* current = fRoot;
while (current != NULL) {
if (current->fID == node->fID) {
found = true;
break;
}
if (current->fID < node->fID) {
previous = current;
current = current->fRight;
} else {
previous = current;
current = current->fLeft;
}
}
if (!found)
return;
if (previous == NULL)
fRoot = NULL;
else if (current->fLeft == NULL && current->fRight == NULL) {
if (previous->fID < node->fID)
previous->fRight = NULL;
else
previous->fLeft = NULL;
} else if (current->fLeft != NULL && current->fRight == NULL) {
if (previous->fID < node->fID)
previous->fRight = current->fLeft;
else
previous->fLeft = current->fLeft;
} else if (current->fLeft == NULL && current->fRight != NULL) {
if (previous->fID < node->fID)
previous->fRight = current->fRight;
else
previous->fLeft = current->fRight;
} else {
ServerNode* left_prev = current;
ServerNode* left = current->fLeft;
while (left->fLeft != NULL) {
left_prev = left;
left = left->fLeft;
}
if (previous->fID < node->fID)
previous->fRight = left;
else
previous->fLeft = left;
left_prev->fLeft = NULL;
}
}
ServerNode*
ServerManager::_Insert(ServerNode* node)
{
ASSERT(node != NULL);
ServerNode* previous = NULL;
ServerNode* current = fRoot;
while (current != NULL) {
if (current->fID == node->fID)
return current;
if (current->fID < node->fID) {
previous = current;
current = current->fRight;
} else {
previous = current;
current = current->fLeft;
}
}
if (previous == NULL)
fRoot = node;
else if (previous->fID < node->fID)
previous->fRight = node;
else
previous->fLeft = node;
return node;
}
@@ -0,0 +1,197 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef RPCSERVER_H
#define RPCSERVER_H
#include <condition_variable.h>
#include <lock.h>
#include "Connection.h"
#include "RPCCall.h"
#include "RPCCallback.h"
#include "RPCReply.h"
namespace RPC {
struct Request {
uint32 fXID;
ConditionVariable fEvent;
bool fDone;
Reply** fReply;
status_t fError;
Request* fNext;
};
class RequestManager {
public:
RequestManager();
~RequestManager();
void AddRequest(Request* request);
Request* FindRequest(uint32 xid);
private:
mutex fLock;
// Neither SinglyLinkedList nor DoublyLinkedList is what we want
// here. And DoublyLinkedQueue is not even a queue.
Request* fQueueHead;
Request* fQueueTail;
};
class ProgramData {
public:
virtual status_t ProcessCallback(CallbackRequest* request,
Connection* connection) = 0;
virtual ~ProgramData() { }
};
class Server {
public:
Server(Connection* connection,
PeerAddress* address);
virtual ~Server();
status_t SendCallAsync(Call* call, Reply** reply,
Request** request);
status_t ResendCallAsync(Call* call,
Request* request);
inline status_t WaitCall(Request* request,
bigtime_t time);
inline status_t CancelCall(Request* request);
status_t WakeCall(Request* request);
status_t Repair();
inline const PeerAddress& ID() const;
inline PeerAddress LocalID() const;
inline ProgramData* PrivateData();
inline void SetPrivateData(ProgramData* privateData);
Callback* GetCallback();
private:
inline uint32 _GetXID();
status_t _StartListening();
status_t _Listener();
static status_t _ListenerThreadStart(void* object);
thread_id fThread;
bool fThreadCancel;
status_t fThreadError;
RequestManager fRequests;
Connection* fConnection;
const PeerAddress* fAddress;
ProgramData* fPrivateData;
mutex fCallbackLock;
Callback* fCallback;
uint32 fRepairCount;
mutex fRepairLock;
vint32 fXID;
};
inline status_t
Server::WaitCall(Request* request, bigtime_t time)
{
if (request->fDone)
return B_OK;
return request->fEvent.Wait(B_RELATIVE_TIMEOUT, time);
}
inline status_t
Server::CancelCall(Request* request)
{
fRequests.FindRequest(request->fXID);
return B_OK;
}
inline const PeerAddress&
Server::ID() const
{
return *fAddress;
}
inline PeerAddress
Server::LocalID() const
{
PeerAddress addr;
memset(&addr, 0, sizeof(addr));
fConnection->GetLocalAddress(&addr);
return addr;
}
inline ProgramData*
Server::PrivateData()
{
return fPrivateData;
}
inline void
Server::SetPrivateData(ProgramData* privateData)
{
delete fPrivateData;
fPrivateData = privateData;
}
struct ServerNode {
PeerAddress fID;
Server* fServer;
int fRefCount;
ServerNode* fLeft;
ServerNode* fRight;
};
class ServerManager {
public:
ServerManager();
~ServerManager();
status_t Acquire(Server** _server, AddressResolver* resolver,
ProgramData* (*createPrivateData)(Server*));
void Release(Server* server);
private:
status_t _Acquire(Server** _server, const PeerAddress& address,
ProgramData* (*createPrivateData)(Server*));
ServerNode* _Find(const PeerAddress& address);
void _Delete(ServerNode* node);
ServerNode* _Insert(ServerNode* node);
ServerNode* fRoot;
mutex fLock;
};
} // namespace RPC
#endif // RPCSERVER_H
@@ -0,0 +1,119 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "ReplyBuilder.h"
#include "NFS4Defs.h"
#include "RPCCallbackReply.h"
ReplyBuilder::ReplyBuilder(uint32 xid)
:
fStatus(B_OK),
fOpCount(0),
fReply(RPC::CallbackReply::Create(xid))
{
_InitHeader();
}
ReplyBuilder::~ReplyBuilder()
{
delete fReply;
}
void
ReplyBuilder::_InitHeader()
{
fStatusPosition = fReply->Stream().Current();
fReply->Stream().AddUInt(0);
fReply->Stream().AddOpaque(NULL, 0);
fOpCountPosition = fReply->Stream().Current();
fReply->Stream().AddUInt(0);
}
RPC::CallbackReply*
ReplyBuilder::Reply()
{
fReply->Stream().InsertUInt(fStatusPosition, _HaikuErrorToNFS4(fStatus));
fReply->Stream().InsertUInt(fOpCountPosition, fOpCount);
if (fReply == NULL || fReply->Stream().Error() == B_OK)
return fReply;
else
return NULL;
}
status_t
ReplyBuilder::GetAttr(status_t status, int mask, uint64 size, uint64 change)
{
if (fStatus != B_OK)
return B_ERROR;
fReply->Stream().AddUInt(OpCallbackGetAttr);
fReply->Stream().AddUInt(_HaikuErrorToNFS4(fStatus));
fStatus = status;
if (status == B_OK) {
uint32 bitmap = 0;
if ((mask & CallbackAttrChange) != 0)
bitmap |= 1 << FATTR4_CHANGE;
if ((mask & CallbackAttrSize) != 0)
bitmap |= 1 << FATTR4_SIZE;
fReply->Stream().AddUInt(1);
fReply->Stream().AddUInt(bitmap);
XDR::WriteStream str;
if ((mask & CallbackAttrChange) != 0)
str.AddUHyper(change);
if ((mask & CallbackAttrSize) != 0)
str.AddUHyper(size);
fReply->Stream().AddOpaque(str);
}
fOpCount++;
return B_OK;
}
status_t
ReplyBuilder::Recall(status_t status)
{
if (fStatus != B_OK)
return B_ERROR;
fReply->Stream().AddUInt(OpCallbackRecall);
fReply->Stream().AddUInt(_HaikuErrorToNFS4(fStatus));
fStatus = status;
fOpCount++;
return B_OK;
}
uint32
ReplyBuilder::_HaikuErrorToNFS4(status_t error)
{
switch (error) {
case B_OK: return NFS4_OK;
case B_FILE_NOT_FOUND: return NFS4ERR_BADHANDLE;
case B_NOT_SUPPORTED: return NFS4ERR_OP_ILLEGAL;
default: return NFS4ERR_RESOURCE;
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef REPLYBUILDER_H
#define REPLYBUILDER_H
#include <SupportDefs.h>
#include "RPCCallbackReply.h"
#include "XDR.h"
class ReplyBuilder {
public:
ReplyBuilder(uint32 xid);
~ReplyBuilder();
RPC::CallbackReply* Reply();
status_t GetAttr(status_t status, int mask,
uint64 size, uint64 change);
status_t Recall(status_t status);
private:
void _InitHeader();
static uint32 _HaikuErrorToNFS4(status_t error);
status_t fStatus;
XDR::Stream::Position fStatusPosition;
uint32 fOpCount;
XDR::Stream::Position fOpCountPosition;
RPC::CallbackReply* fReply;
};
#endif // REPLYBUILDER_H
@@ -0,0 +1,845 @@
/*
* 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 <util/kernel_cpp.h>
#include "Cookie.h"
FSLocation::~FSLocation()
{
free(const_cast<char*>(fRootPath));
for (uint32 i = 0; i < fCount; i++)
free(const_cast<char*>(fLocations[i]));
delete[] fLocations;
}
FSLocations::~FSLocations()
{
free(const_cast<char*>(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(OpLockU);
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;
return fReply->Stream().IsEOF() ? B_BAD_VALUE : 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;
}
static const char*
sFlattenPathname(XDR::ReadStream& str)
{
uint32 count = str.GetUInt();
char* pathname = NULL;
uint32 size = 0;
for (uint32 i = 0; i < count; i++) {
const char* path = str.GetString();
size += strlen(path) + 1;
if (pathname == NULL) {
pathname = reinterpret_cast<char*>(malloc(strlen(path + 1)));
pathname[0] = '\0';
} else {
*pathname++ = '/';
pathname = reinterpret_cast<char*>(realloc(pathname, size));
}
strcat(pathname, path);
free(const_cast<char*>(path));
}
return pathname;
}
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;
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) {
delete[] bitmap;
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 = sFlattenPathname(stream);
locs->fCount = stream.GetUInt();
locs->fLocations = new FSLocation[locs->fCount];
for (uint32 i = 0; i < locs->fCount; i++) {
locs->fLocations[i].fRootPath = sFlattenPathname(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++;
}
delete[] bitmap;
*count = attr_count;
*attrs = values;
return str.IsEOF() ? B_BAD_VALUE : 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;
}
}
@@ -0,0 +1,240 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef REPLYINTERPRETER_H
#define REPLYINTERPRETER_H
#include <SupportDefs.h>
#include "FileInfo.h"
#include "NFS4Defs.h"
#include "RPCReply.h"
struct FSLocation {
const char* fRootPath;
const char** fLocations;
uint32 fCount;
~FSLocation();
};
struct FSLocations {
const char* fRootPath;
FSLocation* fLocations;
uint32 fCount;
~FSLocations();
};
struct AttrValue {
AttrValue();
~AttrValue();
uint8 fAttribute;
bool fFreePointer;
union {
uint32 fValue32;
uint64 fValue64;
void* fPointer;
FSLocations* fLocations;
} fData;
};
struct DirEntry {
const char* fName;
AttrValue* fAttrs;
uint32 fAttrCount;
DirEntry();
~DirEntry();
};
class LockInfo;
class ReplyInterpreter {
public:
ReplyInterpreter(RPC::Reply* reply = NULL);
~ReplyInterpreter();
inline status_t SetTo(RPC::Reply* reply);
inline void Reset();
inline uint32 NFS4Error();
status_t Access(uint32* supported, uint32* allowed);
status_t Close();
status_t Commit();
status_t Create(uint64* before, uint64* after, bool& atomic);
inline status_t DelegReturn();
status_t GetAttr(AttrValue** attrs, uint32* count);
status_t GetFH(FileHandle* fh);
status_t Link(uint64* before, uint64* after, bool& atomic);
status_t Lock(LockInfo* linfo);
status_t LockT(uint64* pos, uint64* len, LockType* type);
status_t LockU(LockInfo* linfo);
inline status_t LookUp();
inline status_t LookUpUp();
inline status_t Nverify();
status_t Open(uint32* id, uint32* seq, bool* confirm,
OpenDelegationData* delegData,
ChangeInfo* changeInfo = NULL);
inline status_t OpenAttrDir();
status_t OpenConfirm(uint32* stateSeq);
inline status_t PutFH();
inline status_t PutRootFH();
status_t Read(void* buffer, uint32* size, bool* eof);
status_t ReadDir(uint64* cookie, uint64* cookieVerf,
DirEntry** dirents, uint32* count, bool* eof);
status_t ReadLink(void* buffer, uint32* size, uint32 maxSize);
status_t Remove(uint64* before, uint64* after, bool& atomic);
status_t Rename(uint64* fromBefore, uint64* fromAfter,
bool& fromAtomic, uint64* toBefore, uint64* toAfter,
bool& toAtomic);
inline status_t Renew();
inline status_t SaveFH();
status_t SetAttr();
status_t SetClientID(uint64* clientid, uint64* verifier);
inline status_t SetClientIDConfirm();
inline status_t Verify();
status_t Write(uint32* size);
inline status_t ReleaseLockOwner();
private:
void _ParseHeader();
status_t _DecodeAttrs(XDR::ReadStream& stream, AttrValue** attrs,
uint32* count);
status_t _OperationError(Opcode op);
static status_t _NFS4ErrorToHaiku(uint32 x);
uint32 fNFS4Error;
bool fDecodeError;
RPC::Reply* fReply;
};
inline status_t
ReplyInterpreter::SetTo(RPC::Reply* _reply)
{
if (fReply != NULL)
return B_DONT_DO_THAT;
fDecodeError = false;
fReply = _reply;
if (fReply != NULL)
_ParseHeader();
return B_OK;
}
inline void
ReplyInterpreter::Reset()
{
delete fReply;
fReply = NULL;
fDecodeError = false;
}
inline uint32
ReplyInterpreter::NFS4Error()
{
return fNFS4Error;
}
inline status_t
ReplyInterpreter::DelegReturn()
{
return _OperationError(OpDelegReturn);
}
inline status_t
ReplyInterpreter::LookUp()
{
return _OperationError(OpLookUp);
}
inline status_t
ReplyInterpreter::LookUpUp()
{
return _OperationError(OpLookUpUp);
}
inline status_t
ReplyInterpreter::OpenAttrDir()
{
return _OperationError(OpOpenAttrDir);
}
inline status_t
ReplyInterpreter::Nverify()
{
return _OperationError(OpNverify);
}
inline status_t
ReplyInterpreter::PutFH()
{
return _OperationError(OpPutFH);
}
inline status_t
ReplyInterpreter::PutRootFH()
{
return _OperationError(OpPutRootFH);
}
inline status_t
ReplyInterpreter::Renew()
{
return _OperationError(OpRenew);
}
inline status_t
ReplyInterpreter::SaveFH()
{
return _OperationError(OpSaveFH);
}
inline status_t
ReplyInterpreter::SetClientIDConfirm()
{
return _OperationError(OpSetClientIDConfirm);
}
inline status_t
ReplyInterpreter::Verify()
{
return _OperationError(OpVerify);
}
inline status_t
ReplyInterpreter::ReleaseLockOwner()
{
return _OperationError(OpReleaseLockOwner);
}
#endif // REPLYINTERPRETER_H
@@ -0,0 +1,158 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Request.h"
#include "FileSystem.h"
#include "Inode.h"
status_t
Request::Send(Cookie* cookie)
{
switch (fServer->ID().fProtocol) {
case IPPROTO_UDP: return _SendUDP(cookie);
case IPPROTO_TCP: return _SendTCP(cookie);
}
return B_BAD_VALUE;
}
status_t
Request::_SendUDP(Cookie* cookie)
{
RPC::Reply* rpl = NULL;
RPC::Request* rpc;
status_t result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
if (result != B_OK)
return result;
if (cookie != NULL)
cookie->RegisterRequest(rpc);
int requestTimeout = sSecToBigTime(60);
int retryLimit = 0;
bool hard = true;
if (fFileSystem != NULL) {
requestTimeout = fFileSystem->GetConfiguration().fRequestTimeout;
retryLimit = fFileSystem->GetConfiguration().fRetryLimit;
hard = fFileSystem->GetConfiguration().fHard;
}
result = fServer->WaitCall(rpc, requestTimeout);
if (result != B_OK) {
int attempts = 1;
while (result != B_OK && (hard || attempts++ < retryLimit)) {
result = fServer->ResendCallAsync(fBuilder.Request(), rpc);
if (result != B_OK) {
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
return result;
}
result = fServer->WaitCall(rpc, requestTimeout);
}
if (result != B_OK) {
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
fServer->CancelCall(rpc);
delete rpc;
return result;
}
}
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
if (rpc->fError != B_OK) {
delete rpl;
result = rpc->fError;
delete rpc;
return result;
} else {
fReply.SetTo(rpl);
delete rpc;
return B_OK;
}
}
status_t
Request::_SendTCP(Cookie* cookie)
{
RPC::Reply* rpl = NULL;
RPC::Request* rpc;
status_t result;
int attempts = 0;
int requestTimeout = sSecToBigTime(60);
int retryLimit = 0;
bool hard = true;
if (fFileSystem != NULL) {
requestTimeout = fFileSystem->GetConfiguration().fRequestTimeout;
retryLimit = fFileSystem->GetConfiguration().fRetryLimit;
hard = fFileSystem->GetConfiguration().fHard;
}
do {
result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
if (result == B_NO_MEMORY)
return result;
else if (result != B_OK) {
fServer->Repair();
continue;
}
if (cookie != NULL)
cookie->RegisterRequest(rpc);
result = fServer->WaitCall(rpc, requestTimeout);
if (result != B_OK) {
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
fServer->CancelCall(rpc);
delete rpc;
fServer->Repair();
}
} while (result != B_OK && (hard || attempts++ < retryLimit));
if (result != B_OK)
return result;
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
if (rpc->fError != B_OK) {
delete rpl;
result = rpc->fError;
delete rpc;
return result;
}
fReply.SetTo(rpl);
delete rpc;
return B_OK;
}
void
Request::Reset()
{
fBuilder.Reset();
fReply.Reset();
}
@@ -0,0 +1,68 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef REQUEST_H
#define REQUEST_H
#include "ReplyInterpreter.h"
#include "RequestBuilder.h"
#include "RPCServer.h"
class Cookie;
class FileSystem;
class Request {
public:
inline Request(RPC::Server* server,
FileSystem* fileSystem);
inline RequestBuilder& Builder();
inline ReplyInterpreter& Reply();
status_t Send(Cookie* cookie = NULL);
void Reset();
private:
status_t _SendUDP(Cookie* cookie);
status_t _SendTCP(Cookie* cookie);
RPC::Server* fServer;
FileSystem* fFileSystem;
RequestBuilder fBuilder;
ReplyInterpreter fReply;
};
inline
Request::Request(RPC::Server* server, FileSystem* fileSystem)
:
fServer(server),
fFileSystem(fileSystem)
{
ASSERT(server != NULL);
}
inline RequestBuilder&
Request::Builder()
{
return fBuilder;
}
inline ReplyInterpreter&
Request::Reply()
{
return fReply;
}
#endif // REQUEST_H
@@ -0,0 +1,917 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RequestBuilder.h"
#include <errno.h>
#include <string.h>
#include "Cookie.h"
#include "OpenState.h"
#include "RPCCallback.h"
#include "RPCCallbackServer.h"
RequestBuilder::RequestBuilder(Procedure proc)
:
fOpCount(0),
fProcedure(proc),
fRequest(NULL)
{
_InitHeader();
}
RequestBuilder::~RequestBuilder()
{
delete fRequest;
}
void
RequestBuilder::_InitHeader()
{
fRequest = RPC::Call::Create(fProcedure, RPC::Auth::CreateSys(),
RPC::Auth::CreateNone());
if (fRequest == NULL)
return;
if (fProcedure == ProcCompound) {
fRequest->Stream().AddOpaque(NULL, 0);
fRequest->Stream().AddUInt(0);
fOpCountPosition = fRequest->Stream().Current();
fRequest->Stream().AddUInt(0);
}
}
status_t
RequestBuilder::Access()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpAccess);
fRequest->Stream().AddUInt(ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY
| ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Close(uint32 seq, const uint32* id, uint32 stateSeq)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpClose);
fRequest->Stream().AddUInt(seq);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Commit(uint64 offset, uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpCommit);
fRequest->Stream().AddUHyper(offset);
fRequest->Stream().AddUInt(count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Create(FileType type, const char* name, AttrValue* attr,
uint32 count, const char* path)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
if (type == NF4LNK && path == NULL)
return B_BAD_VALUE;
if (name == NULL)
return B_BAD_VALUE;
if (type == NF4BLK || type == NF4CHR)
return B_BAD_VALUE;
fRequest->Stream().AddUInt(OpCreate);
fRequest->Stream().AddUInt(type);
if (type == NF4LNK)
fRequest->Stream().AddString(path);
fRequest->Stream().AddString(name);
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::DelegReturn(const uint32* id, uint32 seq)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpDelegReturn);
fRequest->Stream().AddUInt(seq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::GetAttr(Attribute* attrs, uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpGetAttr);
_AttrBitmap(fRequest->Stream(), attrs, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::GetFH()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpGetFH);
fOpCount++;
return B_OK;
}
void
RequestBuilder::_GenerateLockOwner(XDR::WriteStream& stream,
OpenState* state, LockOwner* owner)
{
stream.AddUHyper(state->fClientID);
uint64 lockOwner[2];
lockOwner[0] = owner->fOwner;
lockOwner[1] = state->fInfo.fFileId;
stream.AddOpaque(lockOwner, sizeof(lockOwner));
}
status_t
RequestBuilder::Lock(OpenState* state, LockInfo* lock, uint32* sequence,
bool reclaim)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLock);
fRequest->Stream().AddInt(lock->fType);
fRequest->Stream().AddBoolean(reclaim);
fRequest->Stream().AddUHyper(lock->fStart);
fRequest->Stream().AddUHyper(lock->fLength);
if (lock->fOwner->fStateId[0] == 0 && lock->fOwner->fStateId[1] == 0
&& lock->fOwner->fStateId[2] == 0) {
fRequest->Stream().AddBoolean(true); // new lock owner
// open seq stateid
fRequest->Stream().AddUInt(*sequence);
fRequest->Stream().AddUInt(state->fStateSeq);
fRequest->Stream().AddUInt(state->fStateID[0]);
fRequest->Stream().AddUInt(state->fStateID[1]);
fRequest->Stream().AddUInt(state->fStateID[2]);
// lock seq owner
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
_GenerateLockOwner(fRequest->Stream(), state, lock->fOwner);
} else {
fRequest->Stream().AddBoolean(false); // old lock owner
(*sequence)--;
// lock stateid seq
fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
}
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LockT(LockType type, uint64 pos, uint64 len,
OpenState* state)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLockT);
fRequest->Stream().AddInt(type);
fRequest->Stream().AddUHyper(pos);
fRequest->Stream().AddUHyper(len);
fRequest->Stream().AddUHyper(state->fClientID);
uint32 owner = find_thread(NULL);
fRequest->Stream().AddOpaque(&owner, sizeof(owner));
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LockU(LockInfo* lock)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLockU);
fRequest->Stream().AddInt(lock->fType);
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
fRequest->Stream().AddUHyper(lock->fStart);
fRequest->Stream().AddUHyper(lock->fLength);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Link(const char* name)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
if (name == NULL)
return B_BAD_VALUE;
fRequest->Stream().AddUInt(OpLink);
fRequest->Stream().AddString(name);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LookUp(const char* name)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
if (name == NULL)
return B_BAD_VALUE;
fRequest->Stream().AddUInt(OpLookUp);
fRequest->Stream().AddString(name, strlen(name));
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::LookUpUp()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpLookUpUp);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Nverify(AttrValue* attr, uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpNverify);
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Open(OpenClaim claim, uint32 seq, uint32 access, uint64 id,
OpenCreate oc, uint64 ownerId, const char* name, AttrValue* attr,
uint32 count, bool excl, OpenDelegation delegationType)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpOpen);
fRequest->Stream().AddUInt(seq);
fRequest->Stream().AddUInt(access);
fRequest->Stream().AddUInt(0); // deny none
fRequest->Stream().AddUHyper(id);
char owner[128];
int pos = 0;
*(uint64*)(owner + pos) = ownerId;
pos += sizeof(uint64);
fRequest->Stream().AddOpaque(owner, pos);
fRequest->Stream().AddUInt(oc);
if (oc == OPEN4_CREATE) {
fRequest->Stream().AddInt(excl ? GUARDED4 : UNCHECKED4);
_EncodeAttrs(fRequest->Stream(), attr, count);
}
fRequest->Stream().AddUInt(claim);
switch (claim) {
case CLAIM_NULL:
fRequest->Stream().AddString(name, strlen(name));
break;
case CLAIM_PREVIOUS:
fRequest->Stream().AddUInt(delegationType);
break;
default:
return B_UNSUPPORTED;
}
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::OpenConfirm(uint32 seq, const uint32* id, uint32 stateSeq)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpOpenConfirm);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fRequest->Stream().AddUInt(seq);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::OpenAttrDir(bool create)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpOpenAttrDir);
fRequest->Stream().AddBoolean(create);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::PutFH(const FileHandle& fh)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpPutFH);
fRequest->Stream().AddOpaque(fh.fData, fh.fSize);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::PutRootFH()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpPutRootFH);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Read(const uint32* id, uint32 stateSeq, uint64 pos, uint32 len)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRead);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fRequest->Stream().AddUHyper(pos);
fRequest->Stream().AddUInt(len);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::ReadDir(uint32 count, uint64 cookie, uint64 cookieVerf,
Attribute* attrs, uint32 attrCount)
{
(void)count;
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReadDir);
fRequest->Stream().AddUHyper(cookie);
fRequest->Stream().AddUHyper(cookieVerf);
// consider predicting this values basing on count or buffer size
fRequest->Stream().AddUInt(0x2000);
fRequest->Stream().AddUInt(0x8000);
_AttrBitmap(fRequest->Stream(), attrs, attrCount);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::ReadLink()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReadLink);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Remove(const char* file)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRemove);
fRequest->Stream().AddString(file);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Rename(const char* from, const char* to)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRename);
fRequest->Stream().AddString(from);
fRequest->Stream().AddString(to);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Renew(uint64 clientId)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpRenew);
fRequest->Stream().AddUHyper(clientId);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::SaveFH()
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSaveFH);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::SetAttr(const uint32* id, uint32 stateSeq, AttrValue* attr,
uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSetAttr);
fRequest->Stream().AddUInt(stateSeq);
if (id != NULL) {
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
} else {
fRequest->Stream().AddUInt(0);
fRequest->Stream().AddUInt(0);
fRequest->Stream().AddUInt(0);
}
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::SetClientID(RPC::Server* server)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSetClientID);
uint64 verifier = rand();
verifier = verifier << 32 | rand();
fRequest->Stream().AddUHyper(verifier);
status_t result = _GenerateClientId(fRequest->Stream(), server);
if (result != B_OK)
return result;
fRequest->Stream().AddUInt(0x40000000);
if (server->GetCallback() != NULL) {
ASSERT(server->GetCallback()->CBServer() != NULL);
uint32 id = server->GetCallback()->ID();
PeerAddress local = server->GetCallback()->CBServer()->LocalID();
PeerAddress servAddr = server->LocalID();
servAddr.SetPort(local.Port());
fRequest->Stream().AddString(local.ProtocolString());
char* uAddr = servAddr.UniversalAddress();
if (uAddr == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddString(uAddr);
free(uAddr);
fRequest->Stream().AddUInt(id);
} else {
fRequest->Stream().AddString("");
fRequest->Stream().AddString("");
fRequest->Stream().AddUInt(0);
}
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::_GenerateClientId(XDR::WriteStream& stream,
const RPC::Server* server)
{
char id[512] = "HAIKU:kernel:";
int pos = strlen(id);
PeerAddress local = server->LocalID();
memcpy(id + pos, server->ID().InAddr(), server->ID().InAddrSize());
pos += sizeof(server->ID().InAddrSize());
memcpy(id + pos, local.InAddr(), local.InAddrSize());
pos += sizeof(local.InAddrSize());
*(uint16*)(id + pos) = server->ID().Port();
pos += sizeof(uint16);
*(uint16*)(id + pos) = server->ID().fProtocol;
pos += sizeof(uint16);
stream.AddOpaque(id, pos);
return B_OK;
}
status_t
RequestBuilder::SetClientIDConfirm(uint64 id, uint64 ver)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpSetClientIDConfirm);
fRequest->Stream().AddUHyper(id);
fRequest->Stream().AddUHyper(ver);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Verify(AttrValue* attr, uint32 count)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpVerify);
_EncodeAttrs(fRequest->Stream(), attr, count);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::Write(const uint32* id, uint32 stateSeq, const void* buffer,
uint64 pos, uint32 len, bool stable)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpWrite);
fRequest->Stream().AddUInt(stateSeq);
fRequest->Stream().AddUInt(id[0]);
fRequest->Stream().AddUInt(id[1]);
fRequest->Stream().AddUInt(id[2]);
fRequest->Stream().AddUHyper(pos);
fRequest->Stream().AddInt(stable ? FILE_SYNC4 : UNSTABLE4);
fRequest->Stream().AddOpaque(buffer, len);
fOpCount++;
return B_OK;
}
status_t
RequestBuilder::ReleaseLockOwner(OpenState* state, LockOwner* owner)
{
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReleaseLockOwner);
_GenerateLockOwner(fRequest->Stream(), state, owner);
fOpCount++;
return B_OK;
}
RPC::Call*
RequestBuilder::Request()
{
if (fProcedure == ProcCompound)
fRequest->Stream().InsertUInt(fOpCountPosition, fOpCount);
if (fRequest == NULL || fRequest->Stream().Error() == B_OK)
return fRequest;
else
return NULL;
}
void
RequestBuilder::_AttrBitmap(XDR::WriteStream& stream, Attribute* attrs,
uint32 count)
{
// 2 is safe in NFS4, not in NFS4.1 though
uint32 bitmap[2];
memset(bitmap, 0, sizeof(bitmap));
for (uint32 i = 0; i < count; i++) {
bitmap[attrs[i] / 32] |= 1 << attrs[i] % 32;
}
uint32 bcount = bitmap[1] != 0 ? 2 : 1;
stream.AddUInt(bcount);
for (uint32 i = 0; i < bcount; i++)
stream.AddUInt(bitmap[i]);
}
void
RequestBuilder::_EncodeAttrs(XDR::WriteStream& stream, AttrValue* attr,
uint32 count)
{
if (count == 0) {
stream.AddUInt(0);
stream.AddOpaque(NULL, 0);
return;
}
Attribute* attrs
= reinterpret_cast<Attribute*>(malloc(sizeof(Attribute) * count));
for (uint32 i = 0; i < count; i++)
attrs[i] = static_cast<Attribute>(attr[i].fAttribute);
_AttrBitmap(stream, attrs, count);
free(attrs);
uint32 i = 0;
XDR::WriteStream str;
if (i < count && attr[i].fAttribute == FATTR4_TYPE) {
str.AddUInt(attr[i].fData.fValue32);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_SIZE) {
str.AddUHyper(attr[i].fData.fValue64);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_FILEHANDLE) {
FileHandle* fh = reinterpret_cast<FileHandle*>(attr[i].fData.fPointer);
str.AddOpaque(fh->fData, fh->fSize);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_FILEID) {
str.AddUHyper(attr[i].fData.fValue64);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_MODE) {
str.AddUInt(attr[i].fData.fValue32);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_OWNER) {
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_OWNER_GROUP) {
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_TIME_ACCESS_SET) {
str.AddInt(1); // SET_TO_CLIENT_TIME4
struct timespec* ts
= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
str.AddHyper(ts->tv_sec);
str.AddUInt(ts->tv_nsec);
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_TIME_MODIFY_SET) {
str.AddInt(1); // SET_TO_CLIENT_TIME4
struct timespec* ts
= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
str.AddHyper(ts->tv_sec);
str.AddUInt(ts->tv_nsec);
i++;
}
stream.AddOpaque(str);
}
@@ -0,0 +1,122 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef REQUESTBUILDER_H
#define REQUESTBUILDER_H
#include <SupportDefs.h>
#include "FileInfo.h"
#include "NFS4Defs.h"
#include "ReplyInterpreter.h"
#include "RPCCall.h"
#include "RPCServer.h"
#include "XDR.h"
class OpenState;
class LockInfo;
class LockOwner;
class RequestBuilder {
public:
RequestBuilder(Procedure p = ProcCompound);
~RequestBuilder();
inline void Reset(Procedure proc = ProcCompound);
status_t Access();
status_t Close(uint32 seq, const uint32* id,
uint32 stateSeq);
status_t Commit(uint64 offset, uint32 count);
status_t Create(FileType type, const char* name,
AttrValue* attr, uint32 count,
const char* path = NULL);
status_t DelegReturn(const uint32* id, uint32 seq);
status_t GetAttr(Attribute* attrs, uint32 count);
status_t GetFH();
status_t Link(const char* name);
status_t Lock(OpenState* state, LockInfo* lock,
uint32* sequence, bool reclaim = false);
status_t LockT(LockType type, uint64 pos,
uint64 len, OpenState* state);
status_t LockU(LockInfo* lock);
status_t LookUp(const char* name);
status_t LookUpUp();
status_t Nverify(AttrValue* attr, uint32 count);
status_t Open(OpenClaim claim, uint32 seq,
uint32 access, uint64 id, OpenCreate oc,
uint64 ownerId, const char* name,
AttrValue* attr = NULL,
uint32 count = 0, bool excl = false,
OpenDelegation delegType
= OPEN_DELEGATE_NONE);
status_t OpenAttrDir(bool create);
status_t OpenConfirm(uint32 seq, const uint32* id,
uint32 stateSeq);
status_t PutFH(const FileHandle& fh);
status_t PutRootFH();
status_t Read(const uint32* id, uint32 stateSeq,
uint64 pos, uint32 len);
status_t ReadDir(uint32 count, uint64 cookie,
uint64 cookieVerf, Attribute* attrs,
uint32 attrCount);
status_t ReadLink();
status_t Remove(const char* file);
status_t Rename(const char* from, const char* to);
status_t Renew(uint64 clientId);
status_t SaveFH();
status_t SetAttr(const uint32* id, uint32 stateSeq,
AttrValue* attr, uint32 count);
status_t SetClientID(RPC::Server* server);
status_t SetClientIDConfirm(uint64 id, uint64 ver);
status_t Verify(AttrValue* attr, uint32 count);
status_t Write(const uint32* id, uint32 stateSeq,
const void* buffer, uint64 pos,
uint32 len, bool stable = false);
status_t ReleaseLockOwner(OpenState* state,
LockOwner* owner);
RPC::Call* Request();
private:
void _InitHeader();
void _GenerateLockOwner(XDR::WriteStream& stream,
OpenState* state, LockOwner* owner);
status_t _GenerateClientId(XDR::WriteStream& stream,
const RPC::Server* server);
void _EncodeAttrs(XDR::WriteStream& stream,
AttrValue* attr, uint32 count);
void _AttrBitmap(XDR::WriteStream& stream,
Attribute* attrs, uint32 count);
uint32 fOpCount;
XDR::Stream::Position fOpCountPosition;
Procedure fProcedure;
RPC::Call* fRequest;
};
inline void
RequestBuilder::Reset(Procedure proc)
{
fRequest->Stream().Clear();
fOpCount = 0;
fProcedure = proc;
delete fRequest;
_InitHeader();
}
#endif // REQUESTBUILDER_H
@@ -0,0 +1,86 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RequestInterpreter.h"
#include <string.h>
#include <util/kernel_cpp.h>
RequestInterpreter::RequestInterpreter(RPC::CallbackRequest* request)
:
fRequest(request)
{
fOperationCount = fRequest->Stream().GetUInt();
}
RequestInterpreter::~RequestInterpreter()
{
delete fRequest;
}
status_t
RequestInterpreter::GetAttr(FileHandle* handle, int* _mask)
{
if (fLastOperation != OpCallbackGetAttr)
return B_BAD_VALUE;
uint32 size;
const void* ptr = fRequest->Stream().GetOpaque(&size);
handle->fSize = size;
memcpy(handle->fData, ptr, size);
uint32 count = fRequest->Stream().GetUInt();
if (count < 1) {
*_mask = 0;
return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
uint32 bitmap = fRequest->Stream().GetUInt();
uint32 mask = 0;
if ((bitmap & (1 << FATTR4_CHANGE)) != 0)
mask |= CallbackAttrChange;
if ((bitmap & (1 << FATTR4_SIZE)) != 0)
mask |= CallbackAttrSize;
*_mask = mask;
for (uint32 i = 1; i < count; i++)
fRequest->Stream().GetUInt();
return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
status_t
RequestInterpreter::Recall(FileHandle* handle, bool& truncate, uint32* stateSeq,
uint32* stateID)
{
if (fLastOperation != OpCallbackRecall)
return B_BAD_VALUE;
*stateSeq = fRequest->Stream().GetUInt();
stateID[0] = fRequest->Stream().GetUInt();
stateID[1] = fRequest->Stream().GetUInt();
stateID[2] = fRequest->Stream().GetUInt();
truncate = fRequest->Stream().GetBoolean();
uint32 size;
const void* ptr = fRequest->Stream().GetOpaque(&size);
handle->fSize = size;
memcpy(handle->fData, ptr, size);
return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
}
@@ -0,0 +1,55 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef REQUESTINTERPRETER_H
#define REQUESTINTERPRETER_H
#include <SupportDefs.h>
#include "FileInfo.h"
#include "NFS4Defs.h"
#include "RPCCallbackRequest.h"
class RequestInterpreter {
public:
RequestInterpreter(RPC::CallbackRequest* request);
~RequestInterpreter();
inline uint32 OperationCount();
inline uint32 Operation();
status_t GetAttr(FileHandle* handle, int* mask);
status_t Recall(FileHandle* handle, bool& truncate,
uint32* stateSeq, uint32* stateID);
private:
uint32 fOperationCount;
uint32 fLastOperation;
RPC::CallbackRequest* fRequest;
};
inline uint32
RequestInterpreter::OperationCount()
{
return fOperationCount;
}
inline uint32
RequestInterpreter::Operation()
{
fLastOperation = fRequest->Stream().GetUInt();
return fLastOperation;
}
#endif // REQUESTINTERPRETER_H
@@ -0,0 +1,219 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "RootInode.h"
#include <string.h>
#include "MetadataCache.h"
#include "Request.h"
RootInode::RootInode()
:
fInfoCacheExpire(0),
fName(NULL),
fIOSize(0)
{
mutex_init(&fInfoCacheLock, NULL);
}
RootInode::~RootInode()
{
free(const_cast<char*>(fName));
mutex_destroy(&fInfoCacheLock);
}
status_t
RootInode::ReadInfo(struct fs_info* info)
{
ASSERT(info != NULL);
status_t result = _UpdateInfo();
if (result != B_OK)
return result;
memcpy(info, &fInfoCache, sizeof(struct fs_info));
return B_OK;
}
status_t
RootInode::_UpdateInfo(bool force)
{
if (!force && fInfoCacheExpire > time(NULL))
return B_OK;
MutexLocker _(fInfoCacheLock);
if (fInfoCacheExpire > time(NULL))
return B_OK;
do {
RPC::Server* server = fFileSystem->Server();
Request request(server, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
Attribute attr[] = { FATTR4_FILES_FREE, FATTR4_FILES_TOTAL,
FATTR4_MAXREAD, FATTR4_MAXWRITE, FATTR4_SPACE_FREE,
FATTR4_SPACE_TOTAL };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(reply.NFS4Error(), server))
continue;
reply.PutFH();
AttrValue* values;
uint32 count, next = 0;
result = reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
if (count >= next && values[next].fAttribute == FATTR4_FILES_FREE) {
fInfoCache.free_nodes = values[next].fData.fValue64;
next++;
}
if (count >= next && values[next].fAttribute == FATTR4_FILES_TOTAL) {
fInfoCache.total_nodes = values[next].fData.fValue64;
next++;
}
uint64 ioSize = LONGLONG_MAX;
if (count >= next && values[next].fAttribute == FATTR4_MAXREAD) {
ioSize = min_c(ioSize, values[next].fData.fValue64);
next++;
}
if (count >= next && values[next].fAttribute == FATTR4_MAXWRITE) {
ioSize = min_c(ioSize, values[next].fData.fValue64);
next++;
}
if (ioSize == LONGLONG_MAX)
ioSize = 32768;
fInfoCache.io_size = ioSize;
fInfoCache.block_size = ioSize;
fIOSize = ioSize;
if (count >= next && values[next].fAttribute == FATTR4_SPACE_FREE) {
fInfoCache.free_blocks = values[next].fData.fValue64 / ioSize;
next++;
}
if (count >= next && values[next].fAttribute == FATTR4_SPACE_TOTAL) {
fInfoCache.total_blocks = values[next].fData.fValue64 / ioSize;
next++;
}
delete[] values;
break;
} while (true);
fInfoCache.flags = B_FS_IS_PERSISTENT | B_FS_IS_SHARED
| B_FS_SUPPORTS_NODE_MONITORING;
if (fFileSystem->NamedAttrs()
|| fFileSystem->GetConfiguration().fEmulateNamedAttrs)
fInfoCache.flags |= B_FS_HAS_MIME | B_FS_HAS_ATTR;
strncpy(fInfoCache.volume_name, fName, B_FILE_NAME_LENGTH);
fInfoCacheExpire = time(NULL) + MetadataCache::kExpirationTime;
return B_OK;
}
bool
RootInode::ProbeMigration()
{
do {
RPC::Server* server = fFileSystem->Server();
Request request(server, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
req.Access();
status_t result = request.Send();
if (result != B_OK)
continue;
ReplyInterpreter& reply = request.Reply();
if (reply.NFS4Error() == NFS4ERR_MOVED)
return true;
if (HandleErrors(reply.NFS4Error(), server))
continue;
return false;
} while (true);
}
status_t
RootInode::GetLocations(AttrValue** attrv)
{
ASSERT(attrv != NULL);
do {
RPC::Server* server = fFileSystem->Server();
Request request(server, fFileSystem);
RequestBuilder& req = request.Builder();
req.PutFH(fInfo.fHandle);
Attribute attr[] = { FATTR4_FS_LOCATIONS };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (HandleErrors(reply.NFS4Error(), server))
continue;
reply.PutFH();
uint32 count;
result = reply.GetAttr(attrv, &count);
if (result != B_OK)
return result;
if (count < 1)
return B_ERROR;
return B_OK;
} while (true);
return B_OK;
}
const char*
RootInode::Name() const
{
ASSERT(fName != NULL);
return fName;
}
@@ -0,0 +1,74 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef ROOTINODE_H
#define ROOTINODE_H
#include <fs_info.h>
#include "Inode.h"
class RootInode : public Inode {
public:
RootInode();
~RootInode();
virtual const char* Name() const;
inline void SetName(const char* name);
status_t ReadInfo(struct fs_info* info);
inline void MakeInfoInvalid();
inline uint32 IOSize();
bool ProbeMigration();
status_t GetLocations(AttrValue** attr);
private:
struct fs_info fInfoCache;
mutex fInfoCacheLock;
time_t fInfoCacheExpire;
const char* fName;
uint32 fIOSize;
status_t _UpdateInfo(bool force = false);
};
inline void
RootInode::MakeInfoInvalid()
{
fInfoCacheExpire = 0;
}
inline uint32
RootInode::IOSize()
{
if (fIOSize == 0)
_UpdateInfo(true);
return fIOSize;
}
inline void
RootInode::SetName(const char* name)
{
ASSERT(name != NULL);
free(const_cast<char*>(fName));
fName = strdup(name);
}
#endif // ROOTINODE_H
@@ -0,0 +1,41 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "VnodeToInode.h"
Inode*
VnodeToInode::Get()
{
if (fInode == NULL) {
status_t result = fFileSystem->GetInode(fID, &fInode);
if (result != B_OK)
fInode = NULL;
}
return fInode;
}
void
VnodeToInode::Replace(Inode* newInode)
{
WriteLocker _(fLock);
if (fInode != NULL && !IsRoot()) {
fInode->GetFileSystem()->InoIdMap()->MarkRemoved(fID);
delete fInode;
}
fInode = newInode;
if (fInode != NULL) {
ASSERT(fFileSystem == fInode->GetFileSystem());
fInode->GetFileSystem()->InoIdMap()->AddEntry(fInode->fInfo, fID);
}
}
@@ -0,0 +1,134 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef VNODETOINODE_H
#define VNODETOINODE_H
#include <lock.h>
#include <SupportDefs.h>
#include <util/AutoLock.h>
#include "Inode.h"
#include "InodeIdMap.h"
#include "RootInode.h"
class VnodeToInode {
public:
inline VnodeToInode(ino_t id, FileSystem* fileSystem);
inline ~VnodeToInode();
inline void Lock();
inline void Unlock();
inline Inode* GetPointer() const;
Inode* Get();
void Replace(Inode* newInode);
inline void Remove();
inline void Clear();
inline ino_t ID() const;
inline bool IsRoot() const;
private:
ino_t fID;
rw_lock fLock;
Inode* fInode;
FileSystem* fFileSystem;
};
class VnodeToInodeLocking {
public:
inline bool Lock(VnodeToInode* vti)
{
vti->Lock();
return true;
}
inline void Unlock(VnodeToInode* vti)
{
vti->Unlock();
}
};
typedef AutoLocker<VnodeToInode, VnodeToInodeLocking> VnodeToInodeLocker;
inline
VnodeToInode::VnodeToInode(ino_t id, FileSystem* fileSystem)
:
fID(id),
fInode(NULL),
fFileSystem(fileSystem)
{
rw_lock_init(&fLock, NULL);
}
inline
VnodeToInode::~VnodeToInode()
{
Remove();
if (fFileSystem != NULL && !IsRoot())
fFileSystem->InoIdMap()->RemoveEntry(fID);
rw_lock_destroy(&fLock);
}
inline void
VnodeToInode::Lock()
{
rw_lock_read_lock(&fLock);
}
inline void
VnodeToInode::Unlock()
{
rw_lock_read_unlock(&fLock);
}
inline void
VnodeToInode::Remove()
{
Replace(NULL);
}
inline void
VnodeToInode::Clear()
{
WriteLocker _(fLock);
if (!IsRoot())
delete fInode;
fInode = NULL;
}
inline bool
VnodeToInode::IsRoot() const
{
return fInode && fFileSystem && fInode->ID() == fFileSystem->Root()->ID();
}
inline Inode*
VnodeToInode::GetPointer() const
{
return fInode;
}
inline ino_t
VnodeToInode::ID() const
{
return fID;
}
#endif // VNODETOINODE_H
@@ -0,0 +1,197 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "WorkQueue.h"
#include <io_requests.h>
WorkQueue* gWorkQueue = NULL;
WorkQueue::WorkQueue()
:
fQueueSemaphore(create_sem(0, NULL)),
fThreadCancel(create_sem(0, NULL))
{
mutex_init(&fQueueLock, NULL);
fThread = spawn_kernel_thread(&WorkQueue::LaunchWorkingThread,
"NFSv4 Work Queue", B_NORMAL_PRIORITY, this);
if (fThread < B_OK) {
fInitError = fThread;
return;
}
status_t result = resume_thread(fThread);
if (result != B_OK) {
kill_thread(fThread);
fInitError = result;
return;
}
fInitError = B_OK;
}
WorkQueue::~WorkQueue()
{
release_sem(fThreadCancel);
status_t result;
wait_for_thread(fThread, &result);
mutex_destroy(&fQueueLock);
delete_sem(fThreadCancel);
delete_sem(fQueueSemaphore);
}
status_t
WorkQueue::EnqueueJob(JobType type, void* args)
{
WorkQueueEntry* entry = new(std::nothrow) WorkQueueEntry;
if (entry == NULL)
return B_NO_MEMORY;
entry->fType = type;
entry->fArguments = args;
if (type == IORequest)
reinterpret_cast<IORequestArgs*>(args)->fInode->BeginAIOOp();
MutexLocker locker(fQueueLock);
fQueue.InsertAfter(fQueue.Tail(), entry);
locker.Unlock();
release_sem(fQueueSemaphore);
return B_OK;
}
status_t
WorkQueue::LaunchWorkingThread(void* object)
{
ASSERT(object != NULL);
WorkQueue* queue = reinterpret_cast<WorkQueue*>(object);
return queue->WorkingThread();
}
status_t
WorkQueue::WorkingThread()
{
while (true) {
object_wait_info object[2];
object[0].object = fThreadCancel;
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
object[1].object = fQueueSemaphore;
object[1].type = B_OBJECT_TYPE_SEMAPHORE;
object[1].events = B_EVENT_ACQUIRE_SEMAPHORE;
status_t result = wait_for_objects(object, 2);
if (result < B_OK
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
return result;
} else if ((object[1].events & B_EVENT_ACQUIRE_SEMAPHORE) == 0)
continue;
acquire_sem(fQueueSemaphore);
DequeueJob();
}
return B_OK;
}
void
WorkQueue::DequeueJob()
{
MutexLocker locker(fQueueLock);
WorkQueueEntry* entry = fQueue.RemoveHead();
locker.Unlock();
ASSERT(entry != NULL);
void* args = entry->fArguments;
switch (entry->fType) {
case DelegationRecall:
JobRecall(reinterpret_cast<DelegationRecallArgs*>(args));
break;
case IORequest:
JobIO(reinterpret_cast<IORequestArgs*>(args));
break;
}
delete entry;
}
void
WorkQueue::JobRecall(DelegationRecallArgs* args)
{
ASSERT(args != NULL);
args->fDelegation->GetInode()->RecallDelegation(args->fTruncate);
}
void
WorkQueue::JobIO(IORequestArgs* args)
{
ASSERT(args != NULL);
uint64 offset = io_request_offset(args->fRequest);
uint64 length = io_request_length(args->fRequest);
char* buffer = reinterpret_cast<char*>(malloc(length));
if (buffer == NULL) {
notify_io_request(args->fRequest, B_NO_MEMORY);
args->fInode->EndAIOOp();
return;
}
bool eof = false;
uint64 size = 0;
status_t result;
if (io_request_is_write(args->fRequest)) {
if (offset + length > args->fInode->MaxFileSize())
length = args->fInode->MaxFileSize() - offset;
result = read_from_io_request(args->fRequest, buffer, length);
do {
size_t bytesWritten = length - size;
result = args->fInode->WriteDirect(NULL, offset + size,
buffer + size, &bytesWritten);
size += bytesWritten;
} while (size < length && result == B_OK);
} else {
do {
size_t bytesRead = length - size;
result = args->fInode->ReadDirect(NULL, offset + size, buffer,
&bytesRead, &eof);
if (result != B_OK)
break;
result = write_to_io_request(args->fRequest, buffer, bytesRead);
if (result != B_OK)
break;
size += bytesRead;
} while (size < length && result == B_OK && !eof);
}
free(buffer);
notify_io_request(args->fRequest, result);
args->fInode->EndAIOOp();
}
@@ -0,0 +1,82 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef WORKQUEUE_H
#define WORKQUEUE_H
#include <io_requests.h>
#include <lock.h>
#include <SupportDefs.h>
#include <util/DoublyLinkedList.h>
#include "Delegation.h"
#include "Inode.h"
enum JobType {
DelegationRecall,
IORequest
};
struct DelegationRecallArgs {
Delegation* fDelegation;
bool fTruncate;
};
struct IORequestArgs {
io_request* fRequest;
Inode* fInode;
};
struct WorkQueueEntry : public DoublyLinkedListLinkImpl<WorkQueueEntry> {
JobType fType;
void* fArguments;
};
class WorkQueue {
public:
WorkQueue();
~WorkQueue();
inline status_t InitStatus();
status_t EnqueueJob(JobType type, void* args);
protected:
static status_t LaunchWorkingThread(void* object);
status_t WorkingThread();
void DequeueJob();
void JobRecall(DelegationRecallArgs* args);
void JobIO(IORequestArgs* args);
private:
status_t fInitError;
sem_id fQueueSemaphore;
mutex fQueueLock;
DoublyLinkedList<WorkQueueEntry> fQueue;
sem_id fThreadCancel;
thread_id fThread;
};
inline status_t
WorkQueue::InitStatus()
{
return fInitError;
}
extern WorkQueue* gWorkQueue;
#endif // WORKQUEUE_H
@@ -0,0 +1,337 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "XDR.h"
#include <stdlib.h>
#include <string.h>
#include <ByteOrder.h>
using namespace XDR;
Stream::Stream(void* buffer, uint32 size)
:
fBuffer(reinterpret_cast<uint32*>(buffer)),
fSize(size),
fPosition(0)
{
}
Stream::~Stream()
{
}
uint32
Stream::_PositionToSize() const
{
return fPosition * sizeof(uint32);
}
uint32
Stream::_RealSize(uint32 size) const
{
uint32 real_size = size;
if (real_size % 4 != 0)
real_size = ((real_size >> 2) + 1) << 2;
return real_size;
}
ReadStream::ReadStream(void* buffer, uint32 size)
:
Stream(buffer, size),
fEOF(false)
{
}
ReadStream::~ReadStream()
{
}
int32
ReadStream::GetInt()
{
if (_PositionToSize() >= fSize) {
fEOF = true;
return 0;
}
return B_BENDIAN_TO_HOST_INT32(fBuffer[fPosition++]);
}
uint32
ReadStream::GetUInt()
{
if (_PositionToSize() >= fSize) {
fEOF = true;
return 0;
}
return B_BENDIAN_TO_HOST_INT32(fBuffer[fPosition++]);
}
int64
ReadStream::GetHyper()
{
if (_PositionToSize() + sizeof(int64) > fSize) {
fEOF = true;
return 0;
}
int64* ptr = reinterpret_cast<int64*>(fBuffer + fPosition);
fPosition += 2;
return B_BENDIAN_TO_HOST_INT64(*ptr);
}
uint64
ReadStream::GetUHyper()
{
if (_PositionToSize() + sizeof(uint64) > fSize) {
fEOF = true;
return 0;
}
uint64* ptr = reinterpret_cast<uint64*>(fBuffer + fPosition);
fPosition += 2;
return B_BENDIAN_TO_HOST_INT64(*ptr);
}
char*
ReadStream::GetString()
{
if (_PositionToSize() >= fSize) {
fEOF = true;
return NULL;
}
uint32 size;
const void* ptr = GetOpaque(&size);
if (ptr == NULL)
return NULL;
char* str = reinterpret_cast<char*>(malloc(size + 1));
if (str == NULL)
return NULL;
memcpy(str, ptr, size);
str[size] = 0;
return str;
}
const void*
ReadStream::GetOpaque(uint32* size)
{
if (_PositionToSize() >= fSize) {
fEOF = true;
return NULL;
}
void* ptr = NULL;
uint32 s = GetUInt();
if (s != 0) {
ptr = fBuffer + fPosition;
if (_PositionToSize() + s <= fSize)
fPosition += _RealSize(s) / sizeof(uint32);
else {
s = fSize - _PositionToSize();
fPosition = fSize;
}
}
if (size != NULL)
*size = s;
return ptr;
}
WriteStream::WriteStream()
:
Stream(malloc(kInitialSize), kInitialSize),
fError(B_OK)
{
}
WriteStream::WriteStream(const WriteStream& x)
:
Stream(malloc(x.fSize), x.fSize),
fError(x.fError)
{
fPosition = x.fPosition;
memcpy(fBuffer, x.fBuffer, fSize);
}
WriteStream::~WriteStream()
{
free(fBuffer);
}
void
WriteStream::Clear()
{
free(fBuffer);
fSize = kInitialSize;
fBuffer = reinterpret_cast<uint32*>(malloc(fSize));
fError = B_OK;
fPosition = 0;
}
status_t
WriteStream::InsertUInt(Stream::Position pos, uint32 x)
{
if (pos * sizeof(uint32) >= fSize) {
fError = B_BAD_VALUE;
return B_BAD_VALUE;
}
fBuffer[pos] = B_HOST_TO_BENDIAN_INT32(x);
return B_OK;
}
status_t
WriteStream::AddInt(int32 x)
{
status_t err = _CheckResize(sizeof(int32));
if (err != B_OK)
return err;
fBuffer[fPosition++] = B_HOST_TO_BENDIAN_INT32(x);
return B_OK;
}
status_t
WriteStream::AddUInt(uint32 x)
{
status_t err = _CheckResize(sizeof(uint32));
if (err != B_OK)
return err;
fBuffer[fPosition++] = B_HOST_TO_BENDIAN_INT32(x);
return B_OK;
}
status_t
WriteStream::AddHyper(int64 x)
{
status_t err = _CheckResize(sizeof(int64));
if (err != B_OK)
return err;
int64* ptr = reinterpret_cast<int64*>(fBuffer + fPosition);
*ptr = B_HOST_TO_BENDIAN_INT64(x);
fPosition += 2;
return B_OK;
}
status_t
WriteStream::AddUHyper(uint64 x)
{
status_t err = _CheckResize(sizeof(uint64));
if (err != B_OK)
return err;
uint64* ptr = reinterpret_cast<uint64*>(fBuffer + fPosition);
*ptr = B_HOST_TO_BENDIAN_INT64(x);
fPosition += 2;
return B_OK;
}
status_t
WriteStream::AddString(const char* str, uint32 maxlen)
{
uint32 len = strlen(str);
uint32 size = maxlen == 0 ? len : min_c(maxlen, len);
return AddOpaque(str, size);
}
status_t
WriteStream::AddOpaque(const void* ptr, uint32 size)
{
uint32 real_size = _RealSize(size);
status_t err = _CheckResize(real_size + sizeof(uint32));
if (err != B_OK)
return err;
AddUInt(size);
memset(fBuffer + fPosition, 0, real_size);
memcpy(fBuffer + fPosition, ptr, size);
fPosition += real_size / sizeof(int32);
return B_OK;
}
status_t
WriteStream::AddOpaque(const WriteStream& stream)
{
return AddOpaque(stream.Buffer(), stream.Size());
}
status_t
WriteStream::Append(const WriteStream& stream)
{
uint32 size = stream.Size();
status_t err = _CheckResize(size);
if (err != B_OK)
return err;
memcpy(fBuffer + fPosition, stream.Buffer(), size);
fPosition += size / sizeof(int32);
return B_OK;
}
status_t
WriteStream::_CheckResize(uint32 size)
{
if (_PositionToSize() + size <= fSize)
return B_OK;
uint32 new_size = max_c(fSize * 2, fPosition * sizeof(uint32) + size);
void* ptr = realloc(fBuffer, new_size);
if (ptr == NULL) {
fError = B_NO_MEMORY;
return B_NO_MEMORY;
}
fBuffer = reinterpret_cast<uint32*>(ptr);
fSize = new_size;
return B_OK;
}
+166
View File
@@ -0,0 +1,166 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef XDR_H
#define XDR_H
#include <SupportDefs.h>
namespace XDR {
class Stream {
public:
typedef uint32 Position;
virtual ~Stream();
inline const void* Buffer() const;
inline Position Current() const;
protected:
Stream(void* buffer, uint32 size);
inline uint32 _PositionToSize() const;
inline uint32 _RealSize(uint32 size) const;
uint32* fBuffer;
uint32 fSize;
Position fPosition;
};
class ReadStream : public Stream {
public:
ReadStream(void* buffer, uint32 size);
virtual ~ReadStream();
inline void SetPosition(Position position);
inline int Size() const;
int32 GetInt();
uint32 GetUInt();
int64 GetHyper();
uint64 GetUHyper();
inline bool GetBoolean();
char* GetString();
const void* GetOpaque(uint32* size);
inline bool IsEOF() const;
private:
bool fEOF;
};
class WriteStream : public Stream {
public:
WriteStream();
WriteStream(const WriteStream& x);
virtual ~WriteStream();
inline int Size() const;
void Clear();
status_t InsertUInt(Stream::Position pos, uint32 x);
status_t AddInt(int32 x);
status_t AddUInt(uint32 x);
status_t AddHyper(int64 x);
status_t AddUHyper(uint64 x);
inline status_t AddBoolean(bool x);
status_t AddString(const char* str, uint32 maxlen = 0);
status_t AddOpaque(const void* ptr, uint32 size);
status_t AddOpaque(const WriteStream& stream);
status_t Append(const WriteStream& stream);
inline status_t Error() const;
private:
status_t _CheckResize(uint32 size);
status_t fError;
static const uint32 kInitialSize = 64;
};
inline const void*
Stream::Buffer() const
{
return fBuffer;
}
inline Stream::Position
Stream::Current() const
{
return fPosition;
}
inline void
ReadStream::SetPosition(Position position)
{
fPosition = position;
}
inline int
ReadStream::Size() const
{
return fSize;
}
inline bool
ReadStream::IsEOF() const
{
return fEOF;
}
inline bool
ReadStream::GetBoolean()
{
return GetInt() != 0;
}
inline int
WriteStream::Size() const
{
return fPosition * sizeof(uint32);
}
inline status_t
WriteStream::AddBoolean(bool x)
{
return AddInt(static_cast<int32>(x));
}
inline status_t
WriteStream::Error() const
{
return fError;
}
} // namespace XDR
#endif // XDR_H
@@ -0,0 +1,280 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "IdMapper.h"
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <File.h>
#include <FindDirectory.h>
#include <OS.h>
#include <Path.h>
port_id gRequestPort;
port_id gReplyPort;
const char* kNobodyName = "nobody";
uid_t gNobodyId;
const char* kNogroupName = "nobody";
uid_t gNogroupId;
const char* gDomainName = "localdomain";
status_t
SendError(status_t error)
{
return write_port(gReplyPort, MsgError, &error, sizeof(error));
}
status_t
MatchDomain(char* name)
{
char* domain = strchr(name, '@');
if (domain == NULL)
return B_MISMATCHED_VALUES;
if (strcmp(domain + 1, gDomainName) != 0)
return B_BAD_VALUE;
*domain = '\0';
return B_OK;
}
char*
AddDomain(const char* name)
{
uint32 fullLength = strlen(name) + strlen(gDomainName) + 2;
char* fullName = reinterpret_cast<char*>(malloc(fullLength));
if (fullName == NULL)
return NULL;
strcpy(fullName, name);
strcat(fullName, "@");
strcat(fullName, gDomainName);
return fullName;
}
status_t
NameToUID(void* buffer)
{
char* userName = reinterpret_cast<char*>(buffer);
struct passwd* userInfo = NULL;
if (MatchDomain(userName) == B_OK)
userInfo = getpwnam(userName);
if (userInfo == NULL)
return write_port(gReplyPort, MsgReply, &gNobodyId, sizeof(gNobodyId));
return write_port(gReplyPort, MsgReply, &userInfo->pw_uid, sizeof(uid_t));
}
status_t
UIDToName(void* buffer)
{
uid_t userId = *reinterpret_cast<uid_t*>(buffer);
const char* name = NULL;
struct passwd* userInfo = getpwuid(userId);
if (userInfo != NULL) {
name = userInfo->pw_name;
name = AddDomain(name);
}
status_t result;
if (name != NULL) {
result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1);
free(const_cast<char*>(name));
} else {
result = write_port(gReplyPort, MsgReply, kNobodyName,
strlen(kNobodyName) + 1);
}
return result;
}
status_t
NameToGID(void* buffer)
{
char* groupName = reinterpret_cast<char*>(buffer);
struct group* groupInfo = NULL;
if (MatchDomain(groupName) == B_OK)
groupInfo = getgrnam(groupName);
if (groupInfo == NULL) {
return write_port(gReplyPort, MsgReply, &gNogroupId,
sizeof(gNogroupId));
}
return write_port(gReplyPort, MsgReply, &groupInfo->gr_gid, sizeof(gid_t));
}
status_t
GIDToName(void* buffer)
{
gid_t groupId = *reinterpret_cast<gid_t*>(buffer);
const char* name = NULL;
struct group* groupInfo = getgrgid(groupId);
if (groupInfo != NULL) {
name = groupInfo->gr_name;
name = AddDomain(name);
}
status_t result;
if (name != NULL) {
result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1);
free(const_cast<char*>(name));
} else {
result = write_port(gReplyPort, MsgReply, kNogroupName,
strlen(kNogroupName) + 1);
}
return result;
}
status_t
ParseRequest(int32 code, void* buffer)
{
switch (code) {
case MsgNameToUID:
return NameToUID(buffer);
case MsgUIDToName:
return UIDToName(buffer);
case MsgNameToGID:
return NameToGID(buffer);
case MsgGIDToName:
return GIDToName(buffer);
default:
return SendError(B_BAD_VALUE);
}
}
status_t
MainLoop()
{
do {
ssize_t size = port_buffer_size(gRequestPort);
if (size < B_OK)
return 0;
void* buffer = malloc(size);
if (buffer == NULL)
return B_NO_MEMORY;
int32 code;
size = read_port(gRequestPort, &code, buffer, size);
if (size < B_OK) {
free(buffer);
return 0;
}
status_t result = ParseRequest(code, buffer);
free(buffer);
if (result != B_OK)
return 0;
} while (true);
}
status_t
ReadSettings()
{
BPath path;
status_t result = find_directory(B_COMMON_SETTINGS_DIRECTORY, &path);
if (result != B_OK)
return result;
result = path.Append("nfs4_idmapper.conf");
if (result != B_OK)
return result;
BFile file(path.Path(), B_READ_ONLY);
if (file.InitCheck() != B_OK)
return file.InitCheck();
off_t size;
result = file.GetSize(&size);
if (result != B_OK)
return result;
void* buffer = malloc(size);
if (buffer == NULL)
return B_NO_MEMORY;
file.Read(buffer, size);
gDomainName = reinterpret_cast<char*>(buffer);
return B_OK;
}
int
main(int argc, char** argv)
{
gRequestPort = find_port(kRequestPortName);
if (gRequestPort < B_OK) {
fprintf(stderr, "%s\n", strerror(gRequestPort));
return gRequestPort;
}
gReplyPort = find_port(kReplyPortName);
if (gReplyPort < B_OK) {
fprintf(stderr, "%s\n", strerror(gReplyPort));
return gReplyPort;
}
ReadSettings();
struct passwd* userInfo = getpwnam(kNobodyName);
if (userInfo != NULL)
gNobodyId = userInfo->pw_uid;
else
gNobodyId = 0;
struct group* groupInfo = getgrnam(kNogroupName);
if (groupInfo != NULL)
gNogroupId = groupInfo->gr_gid;
else
gNogroupId = 0;
return MainLoop();
}
@@ -0,0 +1,26 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef IDMAPPER_H
#define IDMAPPER_H
enum MsgCode {
MsgError,
MsgReply,
MsgNameToUID,
MsgUIDToName,
MsgNameToGID,
MsgGIDToName
};
static const char* kRequestPortName = "nfs4idmap_request";
static const char* kReplyPortName = "nfs4idmap_reply";
#endif // IDMAPPER_H
@@ -0,0 +1,13 @@
SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;
Application nfs4_idmapper_server
:
IdMapper.cpp
:
be
$(TARGET_LIBSUPC++)
:
nfs4_idmapper_server.rdef
;
@@ -0,0 +1,20 @@
/*
* nfs4_idmapper_server.rdef
*/
resource app_signature "application/x-vnd.Haiku-nfs4_idmapper-server";
resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
resource app_version {
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_ALPHA,
internal = 0,
short_info = "nfs4_idmapper_server",
long_info = "nfs4_idmapper_server ©2012 Haiku, Inc"
};
File diff suppressed because it is too large Load Diff
+1
View File
@@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel network ;
SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols ;
SubInclude HAIKU_TOP src add-ons kernel network devices ;
SubInclude HAIKU_TOP src add-ons kernel network dns_resolver ;
SubInclude HAIKU_TOP src add-ons kernel network notifications ;
SubInclude HAIKU_TOP src add-ons kernel network protocols ;
SubInclude HAIKU_TOP src add-ons kernel network stack ;
@@ -0,0 +1,23 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef DEFINITIONS_H
#define DEFINITIONS_H
const char* kPortNameReq = "dns_resolver_req";
const char* kPortNameRpl = "dns_resolver_rpl";
enum MsgCodes {
MsgReply,
MsgError,
MsgGetAddrInfo,
};
#endif // DEFINITIONS_H
@@ -0,0 +1,4 @@
SubDir HAIKU_TOP src add-ons kernel network dns_resolver ;
SubInclude HAIKU_TOP src add-ons kernel network dns_resolver kernel_add_on ;
SubInclude HAIKU_TOP src add-ons kernel network dns_resolver server ;
@@ -0,0 +1,9 @@
SubDir HAIKU_TOP src add-ons kernel network dns_resolver kernel_add_on ;
UsePrivateKernelHeaders ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
KernelAddon dns_resolver :
dns_resolver.cpp
;
@@ -0,0 +1,241 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include <net/dns_resolver.h>
#include <AutoDeleter.h>
#include <FindDirectory.h>
#include <lock.h>
#include <port.h>
#include <team.h>
#include <util/AutoLock.h>
#include "Definitions.h"
static mutex gPortLock;
static port_id gPortRequest = -1;
static port_id gPortReply = -1;
static const int32 kQueueLength = 1;
static status_t
dns_resolver_repair()
{
status_t result = B_OK;
gPortRequest = create_port(kQueueLength, kPortNameReq);
if (gPortRequest < B_OK)
return gPortRequest;
gPortReply = create_port(kQueueLength, kPortNameRpl);
if (gPortReply < B_OK) {
delete_port(gPortRequest);
return gPortReply;
}
char path[256];
if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast<dev_t>(-1),
false, path, sizeof(path)) != B_OK) {
delete_port(gPortReply);
delete_port(gPortRequest);
return B_NAME_NOT_FOUND;
}
strlcat(path, "/dns_resolver_server", sizeof(path));
const char* args[] = { path, NULL };
thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY,
B_SYSTEM_TEAM, 0);
if (thread < B_OK) {
delete_port(gPortReply);
delete_port(gPortRequest);
return thread;
}
set_port_owner(gPortRequest, thread);
set_port_owner(gPortReply, thread);
result = resume_thread(thread);
if (result != B_OK) {
kill_thread(thread);
delete_port(gPortReply);
delete_port(gPortRequest);
return result;
}
return B_OK;
}
static status_t
dns_resolver_init()
{
mutex_init(&gPortLock, NULL);
return dns_resolver_repair();
}
static status_t
dns_resolver_uninit()
{
delete_port(gPortRequest);
delete_port(gPortReply);
mutex_destroy(&gPortLock);
return B_OK;
}
static void
RelocateEntries(struct addrinfo* addr)
{
char* generalOffset = reinterpret_cast<char*>(addr);
struct addrinfo* current = addr;
while (current != NULL) {
uint64 addrOffset = reinterpret_cast<uint64>(current->ai_addr);
uint64 nameOffset = reinterpret_cast<uint64>(current->ai_canonname);
uint64 nextOffset = reinterpret_cast<uint64>(current->ai_next);
if (current->ai_addr != NULL) {
current->ai_addr
= reinterpret_cast<sockaddr*>(generalOffset + addrOffset);
}
if (current->ai_canonname != NULL)
current->ai_canonname = generalOffset + nameOffset;
if (current->ai_next != NULL) {
current->ai_next
= reinterpret_cast<addrinfo*>(generalOffset + nextOffset);
}
current = current->ai_next;
}
}
static status_t
GetAddrInfo(const char* node, const char* service,
const struct addrinfo* hints, struct addrinfo** res)
{
uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1;
uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1;
uint32 size = nodeSize + serviceSize + sizeof(*hints);
char* buffer = reinterpret_cast<char*>(malloc(size));
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter _(buffer);
off_t off = 0;
if (node != NULL)
strcpy(buffer + off, node);
else
buffer[off] = '\0';
off += nodeSize;
if (service != NULL)
strcpy(buffer + off, service);
else
buffer[off] = '\0';
off += serviceSize;
if (hints != NULL)
memcpy(buffer + off, hints, sizeof(*hints));
else {
struct addrinfo *nullHints
= reinterpret_cast<struct addrinfo*>(buffer + off);
memset(nullHints, 0, sizeof(*nullHints));
nullHints->ai_family = AF_UNSPEC;
}
MutexLocker locker(gPortLock);
do {
status_t result = write_port(gPortRequest, MsgGetAddrInfo, buffer,
size);
if (result != B_OK) {
result = dns_resolver_repair();
if (result != B_OK)
return result;
continue;
}
ssize_t replySize = port_buffer_size(gPortReply);
if (replySize < B_OK) {
result = dns_resolver_repair();
if (result != B_OK)
return result;
continue;
}
void* reply = malloc(replySize);
if (reply == NULL)
return B_NO_MEMORY;
int32 code;
replySize = read_port(gPortReply, &code, reply, replySize);
if (replySize < B_OK) {
result = dns_resolver_repair();
if (result != B_OK) {
free(reply);
return result;
}
continue;
}
struct addrinfo* addr;
switch (code) {
case MsgReply:
addr = reinterpret_cast<struct addrinfo*>(reply);
RelocateEntries(addr);
*res = addr;
return B_OK;
case MsgError:
result = *reinterpret_cast<status_t*>(reply);
free(reply);
return result;
default:
free(reply);
return B_BAD_VALUE;
}
} while (true);
}
static status_t
dns_resolver_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
return dns_resolver_init();
case B_MODULE_UNINIT:
return dns_resolver_uninit();
default:
return B_ERROR;
}
}
static dns_resolver_module sDNSResolverModule = {
{
DNS_RESOLVER_MODULE_NAME,
0,
dns_resolver_std_ops,
},
GetAddrInfo,
};
module_info* modules[] = {
(module_info*)&sDNSResolverModule,
NULL
};
@@ -0,0 +1,16 @@
SubDir HAIKU_TOP src add-ons kernel network dns_resolver server ;
UsePrivateKernelHeaders ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
Application dns_resolver_server
:
main.cpp
:
be $(TARGET_NETWORK_LIBS)
:
dns_resolver_server.rdef
;
@@ -0,0 +1,20 @@
/*
* dns_resolver_server.rdef
*/
resource app_signature "application/x-vnd.Haiku-dns-resolver-server";
resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
resource app_version {
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_ALPHA,
internal = 0,
short_info = "dns_resolver_server",
long_info = "dns_resolver_server ©2012 Haiku, Inc"
};
@@ -0,0 +1,178 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <AutoDeleter.h>
#include <OS.h>
#include <SupportDefs.h>
#include "Definitions.h"
port_id gRequestPort;
port_id gReplyPort;
status_t
Serialize(char** _reply, uint32* _totalSize, const struct addrinfo* ai)
{
uint32 addrsSize = ai == NULL ? 0 : sizeof(addrinfo);
uint32 namesSize = 0;
uint32 socksSize = 0;
const struct addrinfo* current = ai;
while (current != NULL) {
if (current->ai_canonname != NULL)
namesSize += strlen(current->ai_canonname) + 1;
if (current->ai_addr != NULL) {
if (current->ai_family == AF_INET)
socksSize += sizeof(sockaddr_in);
else
socksSize += sizeof(sockaddr_in6);
}
if (current->ai_next != NULL)
addrsSize += sizeof(addrinfo);
current = current->ai_next;
}
uint32 totalSize = addrsSize + namesSize + socksSize;
char* reply = reinterpret_cast<char*>(malloc(totalSize));
if (reply == NULL)
return B_NO_MEMORY;
uint32 addrPos = 0;
uint32 namePos = addrsSize;
uint32 sockPos = addrsSize + namesSize;
struct addrinfo temp;
current = ai;
while (current != NULL) {
memcpy(&temp, current, sizeof(addrinfo));
if (current->ai_canonname != NULL) {
strcpy(reply + namePos, current->ai_canonname);
uint32 nSize = strlen(current->ai_canonname) + 1;
temp.ai_canonname = reinterpret_cast<char*>(namePos);
namePos += nSize;
}
if (current->ai_addr != NULL) {
if (current->ai_family == AF_INET) {
memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in));
temp.ai_addr = reinterpret_cast<sockaddr*>(sockPos);
sockPos += sizeof(sockaddr_in);
} else {
memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in6));
temp.ai_addr = reinterpret_cast<sockaddr*>(sockPos);
sockPos += sizeof(sockaddr_in6);
}
}
addrinfo* next = current->ai_next;
if (next != NULL)
temp.ai_next = reinterpret_cast<addrinfo*>(addrPos) + 1;
else
temp.ai_next = NULL;
memcpy(reply + addrPos, &temp, sizeof(addrinfo));
addrPos += sizeof(addrinfo);
current = next;
}
*_reply = reply;
*_totalSize = totalSize;
return B_OK;
}
status_t
GetAddrInfo(const char* buffer)
{
const char* node = buffer[0] == '\0' ? NULL : buffer;
uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1;
const char* service = buffer[nodeSize] == '\0' ? NULL : buffer + nodeSize;
uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1;
const struct addrinfo* hints
= reinterpret_cast<const addrinfo*>(buffer + nodeSize + serviceSize);
struct addrinfo* ai;
status_t result = getaddrinfo(node, service, hints, &ai);
if (result != B_OK)
return write_port(gReplyPort, MsgError, &result, sizeof(result));
uint32 totalSize;
char* reply;
result = Serialize(&reply, &totalSize, ai);
freeaddrinfo(ai);
if (result != B_OK)
return write_port(gReplyPort, MsgError, &result, sizeof(result));
return write_port(gReplyPort, MsgReply, reply, totalSize);
}
status_t
MainLoop()
{
do {
ssize_t size = port_buffer_size(gRequestPort);
if (size < B_OK)
return 0;
void* buffer = malloc(size);
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter _(buffer);
int32 code;
size = read_port(gRequestPort, &code, buffer, size);
if (size < B_OK)
return 0;
status_t result;
switch (code) {
case MsgGetAddrInfo:
result = GetAddrInfo(reinterpret_cast<char*>(buffer));
default:
result = B_BAD_VALUE;
write_port(gReplyPort, MsgError, &result, sizeof(result));
result = B_OK;
}
if (result != B_OK)
return 0;
} while (true);
}
int
main(int argc, char** argv)
{
gRequestPort = find_port(kPortNameReq);
if (gRequestPort < B_OK) {
fprintf(stderr, "%s\n", strerror(gRequestPort));
return gRequestPort;
}
gReplyPort = find_port(kPortNameRpl);
if (gReplyPort < B_OK) {
fprintf(stderr, "%s\n", strerror(gReplyPort));
return gReplyPort;
}
return MainLoop();
}
+21 -5
View File
@@ -5484,7 +5484,10 @@ file_close(struct file_descriptor* descriptor)
if (status == B_OK) {
// remove all outstanding locks for this team
release_advisory_lock(vnode, NULL);
if (HAS_FS_CALL(vnode, release_lock))
status = FS_CALL(vnode, release_lock, descriptor->cookie, NULL);
else
status = release_advisory_lock(vnode, NULL);
}
return status;
}
@@ -6015,7 +6018,11 @@ common_fcntl(int fd, int op, size_t argument, bool kernel)
case F_GETLK:
if (vnode != NULL) {
status = get_advisory_lock(vnode, &flock);
if (HAS_FS_CALL(vnode, test_lock)) {
status = FS_CALL(vnode, test_lock, descriptor->cookie,
&flock);
} else
status = get_advisory_lock(vnode, &flock);
if (status == B_OK) {
// copy back flock structure
status = user_memcpy((struct flock*)argument, &flock,
@@ -6034,7 +6041,11 @@ common_fcntl(int fd, int op, size_t argument, bool kernel)
if (vnode == NULL) {
status = B_BAD_VALUE;
} else if (flock.l_type == F_UNLCK) {
status = release_advisory_lock(vnode, &flock);
if (HAS_FS_CALL(vnode, release_lock)) {
status = FS_CALL(vnode, release_lock, descriptor->cookie,
&flock);
} else
status = release_advisory_lock(vnode, &flock);
} else {
// the open mode must match the lock type
if (((descriptor->open_mode & O_RWMASK) == O_RDONLY
@@ -6043,8 +6054,13 @@ common_fcntl(int fd, int op, size_t argument, bool kernel)
&& flock.l_type == F_RDLCK))
status = B_FILE_ERROR;
else {
status = acquire_advisory_lock(vnode, -1,
&flock, op == F_SETLKW);
if (HAS_FS_CALL(vnode, acquire_lock)) {
status = FS_CALL(vnode, acquire_lock,
descriptor->cookie, &flock, op == F_SETLKW);
} else {
status = acquire_advisory_lock(vnode, -1,
&flock, op == F_SETLKW);
}
}
}
break;
+3
View File
@@ -4,6 +4,9 @@ UsePrivateHeaders [ FDirName kernel util ] ;
KernelMergeObject kernel_util.o :
AVLTreeBase.cpp
hostname.cpp
inet_addr.c
inet_ntop.c
kernel_cpp.cpp
KernelReferenceable.cpp
khash.cpp
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <FindDirectory.h>
#include <StorageDefs.h>
#include <errno_private.h>
static status_t
get_path(char *path, bool create)
{
status_t status = find_directory(B_COMMON_SETTINGS_DIRECTORY, -1, create,
path, B_PATH_NAME_LENGTH);
if (status != B_OK)
return status;
strlcat(path, "/network", B_PATH_NAME_LENGTH);
if (create)
mkdir(path, 0755);
strlcat(path, "/hostname", B_PATH_NAME_LENGTH);
return B_OK;
}
extern "C" int
sethostname(const char *hostName, size_t nameSize)
{
char path[B_PATH_NAME_LENGTH];
if (get_path(path, false) != B_OK) {
__set_errno(B_ERROR);
return -1;
}
int file = open(path, O_WRONLY | O_CREAT, 0644);
if (file < 0)
return -1;
nameSize = min_c(nameSize, MAXHOSTNAMELEN);
if (write(file, hostName, nameSize) != (ssize_t)nameSize
|| write(file, "\n", 1) != 1) {
close(file);
return -1;
}
close(file);
return 0;
}
extern "C" int
gethostname(char *hostName, size_t nameSize)
{
// look up hostname from network settings hostname file
char path[B_PATH_NAME_LENGTH];
if (get_path(path, false) != B_OK) {
__set_errno(B_ERROR);
return -1;
}
int file = open(path, O_RDONLY);
if (file < 0)
return -1;
nameSize = min_c(nameSize, MAXHOSTNAMELEN);
int length = read(file, hostName, nameSize - 1);
close(file);
if (length < 0)
return -1;
hostName[length] = '\0';
char *end = strpbrk(hostName, "\r\n\t");
if (end != NULL)
end[0] = '\0';
return 0;
}
+198
View File
@@ -0,0 +1,198 @@
/*
* Copyright (c) 1983, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Portions Copyright (c) 1996-1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
/*%
* Ascii internet address interpretation routine.
* The value returned is in network order.
*/
in_addr_t
inet_addr(const char *cp) {
struct in_addr val;
if (inet_aton(cp, &val))
return (val.s_addr);
return (INADDR_NONE);
}
/*%
* Check whether "cp" is a valid ascii representation
* of an Internet address and convert to a binary address.
* Returns 1 if the address is valid, 0 if not.
* This replaces inet_addr, the return value from which
* cannot distinguish between failure and a local broadcast address.
*/
int
inet_aton(const char *cp, struct in_addr *addr) {
u_long val;
int base, n;
char c;
u_int8_t parts[4];
u_int8_t *pp = parts;
int digit;
c = *cp;
for (;;) {
/*
* Collect number up to ``.''.
* Values are specified as for C:
* 0x=hex, 0=octal, isdigit=decimal.
*/
if (!isdigit((unsigned char)c))
return (0);
val = 0; base = 10; digit = 0;
if (c == '0') {
c = *++cp;
if (c == 'x' || c == 'X')
base = 16, c = *++cp;
else {
base = 8;
digit = 1 ;
}
}
for (;;) {
if (isascii(c) && isdigit((unsigned char)c)) {
if (base == 8 && (c == '8' || c == '9'))
return (0);
val = (val * base) + (c - '0');
c = *++cp;
digit = 1;
} else if (base == 16 && isascii(c) &&
isxdigit((unsigned char)c)) {
val = (val << 4) |
(c + 10 - (islower((unsigned char)c) ? 'a' : 'A'));
c = *++cp;
digit = 1;
} else
break;
}
if (c == '.') {
/*
* Internet format:
* a.b.c.d
* a.b.c (with c treated as 16 bits)
* a.b (with b treated as 24 bits)
*/
if (pp >= parts + 3 || val > 0xffU)
return (0);
*pp++ = val;
c = *++cp;
} else
break;
}
/*
* Check for trailing characters.
*/
if (c != '\0' && (!isascii(c) || !isspace((unsigned char)c)))
return (0);
/*
* Did we get a valid digit?
*/
if (!digit)
return (0);
/*
* Concoct the address according to
* the number of parts specified.
*/
n = pp - parts + 1;
switch (n) {
case 1: /*%< a -- 32 bits */
break;
case 2: /*%< a.b -- 8.24 bits */
if (val > 0xffffffU)
return (0);
val |= parts[0] << 24;
break;
case 3: /*%< a.b.c -- 8.8.16 bits */
if (val > 0xffffU)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16);
break;
case 4: /*%< a.b.c.d -- 8.8.8.8 bits */
if (val > 0xffU)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
break;
}
if (addr != NULL)
addr->s_addr = htonl(val);
return (1);
}
+202
View File
@@ -0,0 +1,202 @@
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "$Id: inet_ntop.c,v 1.5 2005/11/03 22:59:52 marka Exp $";
#endif /* LIBC_SCCS and not lint */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#ifdef SPRINTF_CHAR
# define SPRINTF(x) strlen(sprintf/**/x)
#else
# define SPRINTF(x) ((size_t)sprintf x)
#endif
/*%
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static const char *inet_ntop4 __P((const u_char *src, char *dst, size_t size));
static const char *inet_ntop6 __P((const u_char *src, char *dst, size_t size));
/* char *
* inet_ntop(af, src, dst, size)
* convert a network format address to presentation format.
* return:
* pointer to presentation format address (`dst'), or NULL (see errno).
* author:
* Paul Vixie, 1996.
*/
const char *
inet_ntop(af, src, dst, size)
int af;
const void *src;
char *dst;
socklen_t size;
{
switch (af) {
case AF_INET:
return (inet_ntop4(src, dst, size));
case AF_INET6:
return (inet_ntop6(src, dst, size));
default:
errno = EAFNOSUPPORT;
return (NULL);
}
/* NOTREACHED */
}
/* const char *
* inet_ntop4(src, dst, size)
* format an IPv4 address
* return:
* `dst' (as a const)
* notes:
* (1) uses no statics
* (2) takes a u_char* not an in_addr as input
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop4(src, dst, size)
const u_char *src;
char *dst;
size_t size;
{
static const char fmt[] = "%u.%u.%u.%u";
char tmp[sizeof "255.255.255.255"];
if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) >= size) {
errno = ENOSPC;
return (NULL);
}
strcpy(dst, tmp);
return (dst);
}
/* const char *
* inet_ntop6(src, dst, size)
* convert IPv6 binary address into presentation (printable) format
* author:
* Paul Vixie, 1996.
*/
static const char *
inet_ntop6(src, dst, size)
const u_char *src;
char *dst;
size_t size;
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
struct { int base, len; } best, cur;
u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
int i;
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof words);
for (i = 0; i < NS_IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
best.len = 0;
cur.base = -1;
cur.len = 0;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len)) {
if (i == best.base)
*tp++ = ':';
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0)
*tp++ = ':';
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 && (best.len == 6 ||
(best.len == 7 && words[7] != 0x0001) ||
(best.len == 5 && words[5] == 0xffff))) {
if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
return (NULL);
tp += strlen(tp);
break;
}
tp += SPRINTF((tp, "%x", words[i]));
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) ==
(NS_IN6ADDRSZ / NS_INT16SZ))
*tp++ = ':';
*tp++ = '\0';
/*
* Check for overflow, copy, and we're done.
*/
if ((size_t)(tp - tmp) > size) {
errno = ENOSPC;
return (NULL);
}
strcpy(dst, tmp);
return (dst);
}