diff --git a/build/jam/BuildSetup b/build/jam/BuildSetup index 4c612b0d9a..57331fd52f 100644 --- a/build/jam/BuildSetup +++ b/build/jam/BuildSetup @@ -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 ; diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 6dbb81f3d2..a8422655e0 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -18,7 +18,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures hd head hey hostname id ident ifconfig 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 tty cpuidle@x86 ; + locked_pool mpu401 scsi_periph 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 : 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 diff --git a/headers/os/drivers/fs_interface.h b/headers/os/drivers/fs_interface.h index 03d867b131..1b3f0be8f7 100644 --- a/headers/os/drivers/fs_interface.h +++ b/headers/os/drivers/fs_interface.h @@ -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 { diff --git a/headers/private/kernel/util/AVLTreeMap.h b/headers/private/kernel/util/AVLTreeMap.h index ea436be3c1..051f4e7fdd 100644 --- a/headers/private/kernel/util/AVLTreeMap.h +++ b/headers/private/kernel/util/AVLTreeMap.h @@ -269,6 +269,7 @@ _AVL_TREE_MAP_CLASS_NAME::MakeEmpty() { AVLTreeNode* root = fTree.Root(); _FreeTree(root); + fTree.MakeEmpty(); } diff --git a/headers/private/net/dns_resolver.h b/headers/private/net/dns_resolver.h new file mode 100644 index 0000000000..d868ca0381 --- /dev/null +++ b/headers/private/net/dns_resolver.h @@ -0,0 +1,58 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DNS_RESOLVER_H +#define DNS_RESOLVER_H + + +#include +#include + +#include + + +#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(&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 + diff --git a/src/add-ons/kernel/file_systems/Jamfile b/src/add-ons/kernel/file_systems/Jamfile index 24fe5eb6a1..25bb03651a 100644 --- a/src/add-ons/kernel/file_systems/Jamfile +++ b/src/add-ons/kernel/file_systems/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.cpp b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp new file mode 100644 index 0000000000..937e45ed1b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp @@ -0,0 +1,798 @@ +/* + * Copyright 2012-2013 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Connection.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + + +#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(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(&fAddress)->sin_port; + break; + case AF_INET6: + port = reinterpret_cast(&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(&fAddress)->sin_port = port; + break; + case AF_INET6: + reinterpret_cast(&fAddress)->sin6_port = port; + break; + } +} + +const void* +PeerAddress::InAddr() const +{ + switch (Family()) { + case AF_INET: + return &reinterpret_cast(&fAddress)->sin_addr; + case AF_INET6: + return &reinterpret_cast(&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(&address->fAddress)->sin_port + = fForcedPort; + } else if (fCurrent->ai_family == AF_INET6) { + memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in6)); + reinterpret_cast(&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(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(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(&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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.h b/src/add-ons/kernel/file_systems/nfs4/Connection.h new file mode 100644 index 0000000000..3c01daa6dd --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.h @@ -0,0 +1,146 @@ +/* + * Copyright 2012-2013 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef CONNECTION_H +#define CONNECTION_H + + +#include + +#include +#include + + +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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp new file mode 100644 index 0000000000..e756e75301 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp @@ -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 "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(lock.l_start); + uint64 len = static_cast(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(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.h b/src/add-ons/kernel/file_systems/nfs4/Cookie.h new file mode 100644 index 0000000000..a3d36c9c8c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.h @@ -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 COOKIE_H +#define COOKIE_H + + +#include + +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp b/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp new file mode 100644 index 0000000000..2fce4fde6c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Delegation.h b/src/add-ons/kernel/file_systems/nfs4/Delegation.h new file mode 100644 index 0000000000..807d52617c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Delegation.h @@ -0,0 +1,65 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DELEGATION_H +#define DELEGATION_H + + +#include +#include + +#include "NFS4Object.h" + + +class Inode; + +class Delegation : public NFS4Object, + public DoublyLinkedListLinkImpl { +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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp new file mode 100644 index 0000000000..c40707a35d --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp @@ -0,0 +1,345 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "DirectoryCache.h" + +#include +#include + +#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(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::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::Iterator oldIt + = oldSnapshot->fEntries.GetIterator(); + NameCacheEntry* oldCurrent; + + SinglyLinkedList::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(); + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h new file mode 100644 index 0000000000..357ec6e213 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h @@ -0,0 +1,175 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DIRECTORYCACHE_H +#define DIRECTORYCACHE_H + + +#include +#include +#include +#include +#include + + +class Inode; + +struct NameCacheEntry : + public SinglyLinkedListLinkImpl { + ino_t fNode; + const char* fName; + + NameCacheEntry(const char* name, ino_t node); + NameCacheEntry(const NameCacheEntry& entry); + ~NameCacheEntry(); +}; + +struct DirectoryCacheSnapshot : public KernelReferenceable { + SinglyLinkedList 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& 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 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& +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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp b/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp new file mode 100644 index 0000000000..5b7a57d776 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#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(fName)); + fName = strdup(name); + if (fName == NULL) + return B_NO_MEMORY; + + free(const_cast(fPath)); + fPath = NULL; + if (dirPath != NULL) { + char* path = reinterpret_cast(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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileInfo.h b/src/add-ons/kernel/file_systems/nfs4/FileInfo.h new file mode 100644 index 0000000000..4182dcc331 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileInfo.h @@ -0,0 +1,188 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef FILEINFO_H +#define FILEINFO_H + + +#include +#include + +#include + + +#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(fName)); + free(const_cast(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(fName)); + fName = strdup(fi.fName); + + free(const_cast(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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp new file mode 100644 index 0000000000..309f572d28 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp @@ -0,0 +1,393 @@ +/* + * 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 + +#include +#include + +#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(fServer->PrivateData()); + if (server != NULL) + server->RemoveFileSystem(this); + + mutex_destroy(&fDelegationLock); + mutex_destroy(&fOpenLock); + mutex_destroy(&fOpenOwnerLock); + mutex_destroy(&fCreateFileLock); + + free(const_cast(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 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(values[2].fData.fPointer); + + if (count == 4 && values[3].fAttribute == FATTR4_FS_LOCATIONS) { + FSLocations* locs + = reinterpret_cast(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(inode)->SetName(name); + } else if (fsPath[0] != '\0') + reinterpret_cast(inode)->SetName(fsPath); + else { + char* address = serv->ID().UniversalAddress(); + if (address != NULL) + reinterpret_cast(inode)->SetName(address); + else + reinterpret_cast(inode)->SetName("NFS4 Share"); + free(address); + } + + fs->fRoot = reinterpret_cast(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(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(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(server->PrivateData()); + old->RemoveFileSystem(this); + NFSServer()->AddFileSystem(this); + + gRPCServerManager->Release(server); + + return B_OK; +} + + +DoublyLinkedList& +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& +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::Iterator it; + it = fHandleToDelegation.Find(handle); + if (!it.HasCurrent()) + return NULL; + + return it.Current(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.h b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h new file mode 100644 index 0000000000..2d961b4f59 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h @@ -0,0 +1,247 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#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 { +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& OpenFilesLock(); + void OpenFilesUnlock(); + inline uint32 OpenFilesCount(); + void AddOpenFile(OpenState* state); + void RemoveOpenFile(OpenState* state); + + DoublyLinkedList& 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 fDelegationList; + AVLTreeMap fHandleToDelegation; + + DoublyLinkedList 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(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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/IdMap.cpp b/src/add-ons/kernel/file_systems/nfs4/IdMap.cpp new file mode 100644 index 0000000000..a779469eb7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/IdMap.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "IdMap.h" + +#include +#include +#include +#include + +#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(owner, MsgNameToUID); +} + + +gid_t +IdMap::GetGroupId(const char* ownerGroup) +{ + ASSERT(ownerGroup != NULL); + return _GetValue(ownerGroup, MsgNameToGID); +} + + +char* +IdMap::GetOwner(uid_t user) +{ + return reinterpret_cast(_GetBuffer(user, MsgUIDToName)); +} + + +char* +IdMap::GetOwnerGroup(gid_t group) +{ + return reinterpret_cast(_GetBuffer(group, MsgGIDToName)); +} + + +template +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 +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(-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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/IdMap.h b/src/add-ons/kernel/file_systems/nfs4/IdMap.h new file mode 100644 index 0000000000..daf66446cb --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/IdMap.h @@ -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 IDMAP_H +#define IDMAP_H + + +#include +#include +#include + + +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 + void* _GetBuffer(T value, int32 code); + + template + 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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp new file mode 100644 index 0000000000..fe3d60d69c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp @@ -0,0 +1,981 @@ +/* + * 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 +#include + +#include +#include +#include + +#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(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(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(fromName)); + toNameDeleter.SetTo(const_cast(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(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(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, <ype, &position, &length, conflict); + if (result != B_OK) + return result; + + if (conflict) { + lock->l_type = sLockTypeToHaiku(ltype); + lock->l_start = static_cast(position); + if (length >= OFF_MAX) + lock->l_len = OFF_MAX; + else + lock->l_len = static_cast(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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.h b/src/add-ons/kernel/file_systems/nfs4/Inode.h new file mode 100644 index 0000000000..0c5e8449fe --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.h @@ -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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp b/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp new file mode 100644 index 0000000000..1b3851704c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp @@ -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 +#include + +#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(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(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(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(_buffer); + uint32 pos = 0; + uint32 i = 0; + bool overflow = false; + + if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) { + struct dirent* de = reinterpret_cast(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(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(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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h new file mode 100644 index 0000000000..43ae7994b2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h @@ -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 +#include +#include +#include + +#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 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::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::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::Iterator it = fMap.Find(id); + if (!it.HasCurrent()) + return true; + + return it.Current().fRemoved; +} + + +#endif // INODEIDMAP_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp b/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp new file mode 100644 index 0000000000..974827413b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp @@ -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 + +#include +#include +#include + +#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(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(_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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Jamfile b/src/add-ons/kernel/file_systems/nfs4/Jamfile new file mode 100644 index 0000000000..3d678a9001 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Jamfile @@ -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 ; + diff --git a/src/add-ons/kernel/file_systems/nfs4/MetadataCache.cpp b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.cpp new file mode 100644 index 0000000000..570f6da44b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.cpp @@ -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 + +#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::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::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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/MetadataCache.h b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.h new file mode 100644 index 0000000000..82e44c6496 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.h @@ -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 +#include +#include +#include +#include + + +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 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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h new file mode 100644 index 0000000000..aab14e6596 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.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 +#include + +#include + + +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(sec) * 1000000; +} + + +#endif // NFS4DEFS_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp new file mode 100644 index 0000000000..6118c42175 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp @@ -0,0 +1,1178 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "IdMap.h" +#include "Inode.h" +#include "NFS4Inode.h" +#include "Request.h" + + +status_t +NFS4Inode::GetChangeInfo(uint64* change, bool attrDir) +{ + ASSERT(change != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (attrDir) + req.PutFH(fInfo.fAttrDir); + else + req.PutFH(fInfo.fHandle); + + Attribute attr[] = { FATTR4_CHANGE }; + 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(); + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK || count < 1) + return result; + + // FATTR4_CHANGE is mandatory + *change = values[0].fData.fValue64; + delete[] values; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::CommitWrites() +{ + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Commit(0, 0); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + return reply.Commit(); + } while (true); +} + + +status_t +NFS4Inode::Access(uint32* allowed) +{ + ASSERT(allowed != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Access(); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + return reply.Access(NULL, allowed); + } while (true); +} + + +status_t +NFS4Inode::LookUp(const char* name, uint64* change, uint64* fileID, + FileHandle* handle, bool parent) +{ + ASSERT(name != NULL); + ASSERT(change != NULL); + ASSERT(fileID != NULL); + ASSERT(handle != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (parent) + req.PutFH(fInfo.fParent); + else + req.PutFH(fInfo.fHandle); + + if (change != NULL) { + Attribute dirAttr[] = { FATTR4_CHANGE }; + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + } + + if (!strcmp(name, "..")) + req.LookUpUp(); + else + req.LookUp(name); + + req.GetFH(); + + Attribute attr[] = { 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 (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + AttrValue* values; + uint32 count; + if (change != NULL) { + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + *change = values[0].fData.fValue64; + delete[] values; + } + + if (!strcmp(name, "..")) + result = reply.LookUpUp(); + else + result = reply.LookUp(); + if (result != B_OK) + return result; + + reply.GetFH(handle); + + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + // FATTR4_FSID is mandatory + FileSystemId* fsid + = reinterpret_cast(values[0].fData.fPointer); + if (*fsid != fFileSystem->FsId()) { + delete[] values; + return B_ENTRY_NOT_FOUND; + } + + if (fileID != NULL) { + if (count < 2 || values[1].fAttribute != FATTR4_FILEID) + *fileID = fFileSystem->AllocFileId(); + else + *fileID = values[1].fData.fValue64; + } + + delete[] values; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::Link(Inode* dir, const char* name, ChangeInfo* changeInfo) +{ + ASSERT(dir != NULL); + ASSERT(name != NULL); + ASSERT(changeInfo != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.SaveFH(); + req.PutFH(dir->fInfo.fHandle); + req.Link(name); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + reply.SaveFH(); + reply.PutFH(); + + return reply.Link(&changeInfo->fBefore, &changeInfo->fAfter, + changeInfo->fAtomic); + } while (true); +} + + +status_t +NFS4Inode::ReadLink(void* buffer, size_t* length) +{ + ASSERT(buffer != NULL); + ASSERT(length != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.ReadLink(); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + uint32 size; + result = reply.ReadLink(buffer, &size, *length); + *length = static_cast(size); + + return result; + } while (true); +} + + +status_t +NFS4Inode::GetStat(AttrValue** values, uint32* count, OpenAttrCookie* cookie) +{ + ASSERT(values != NULL); + ASSERT(count != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (cookie != NULL) + req.PutFH(cookie->fOpenState->fInfo.fHandle); + else + req.PutFH(fInfo.fHandle); + + Attribute attr[] = { FATTR4_SIZE, FATTR4_MODE, FATTR4_NUMLINKS, + FATTR4_OWNER, FATTR4_OWNER_GROUP, + FATTR4_TIME_ACCESS, FATTR4_TIME_CREATE, + FATTR4_TIME_METADATA, FATTR4_TIME_MODIFY }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + return reply.GetAttr(values, count); + } while (true); +} + + +status_t +NFS4Inode::WriteStat(OpenState* state, AttrValue* attrs, uint32 attrCount) +{ + ASSERT(attrs != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (state != NULL) { + req.PutFH(state->fInfo.fHandle); + req.SetAttr(state->fStateID, state->fStateSeq, attrs, attrCount); + } else { + req.PutFH(fInfo.fHandle); + req.SetAttr(NULL, 0, attrs, attrCount); + } + + 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.SetAttr(); + + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::RenameNode(Inode* from, Inode* to, const char* fromName, + const char* toName, ChangeInfo* fromChange, ChangeInfo* toChange, + uint64* fileID, bool attribute) +{ + ASSERT(from != NULL); + ASSERT(to != NULL); + ASSERT(fromName != NULL); + ASSERT(toName != NULL); + ASSERT(fromChange != NULL); + ASSERT(toChange != NULL); + + do { + RPC::Server* serv = from->fFileSystem->Server(); + Request request(serv, from->fFileSystem); + RequestBuilder& req = request.Builder(); + + if (attribute) + req.PutFH(from->fInfo.fAttrDir); + else + req.PutFH(from->fInfo.fHandle); + req.SaveFH(); + if (attribute) + req.PutFH(to->fInfo.fAttrDir); + else + req.PutFH(to->fInfo.fHandle); + req.Rename(fromName, toName); + + if (!attribute) { + req.LookUp(toName); + 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(); + + // FileHandle has expired + if (reply.NFS4Error() == NFS4ERR_FHEXPIRED) { + from->fInfo.UpdateFileHandles(from->fFileSystem); + to->fInfo.UpdateFileHandles(to->fFileSystem); + continue; + } + + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + from->fFileSystem->Migrate(serv); + continue; + } + + // need to wait + if (reply.NFS4Error() == NFS4ERR_DELAY) { + snooze_etc(sSecToBigTime(5), B_SYSTEM_TIMEBASE, B_RELATIVE_TIMEOUT); + continue; + } + + reply.PutFH(); + reply.SaveFH(); + reply.PutFH(); + + result = reply.Rename(&fromChange->fBefore, &fromChange->fAfter, + fromChange->fAtomic, &toChange->fBefore, &toChange->fAfter, + toChange->fAtomic); + if (result != B_OK) + return result; + + if (!attribute) { + result = reply.LookUp(); + if (result != B_OK) + return result; + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + if (count == 0) + *fileID = from->fFileSystem->AllocFileId(); + else + *fileID = values[0].fData.fValue64; + + delete[] values; + } + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::CreateFile(const char* name, int mode, int perms, OpenState* state, + ChangeInfo* changeInfo, uint64* fileID, FileHandle* handle, + OpenDelegationData* delegation) +{ + ASSERT(name != NULL); + ASSERT(state != NULL); + ASSERT(changeInfo != NULL); + ASSERT(handle != NULL); + ASSERT(delegation != NULL); + + bool confirm; + status_t result; + + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + state->fClientID = fFileSystem->NFSServer()->ClientId(); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + + AttrValue cattr[2]; + uint32 i = 0; + if ((mode & O_TRUNC) == O_TRUNC) { + cattr[i].fAttribute = FATTR4_SIZE; + cattr[i].fFreePointer = false; + cattr[i].fData.fValue64 = 0; + i++; + } + cattr[i].fAttribute = FATTR4_MODE; + cattr[i].fFreePointer = false; + cattr[i].fData.fValue32 = perms; + i++; + + req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), + state->fClientID, OPEN4_CREATE, fFileSystem->OpenOwner(), name, + cattr, i, (mode & O_EXCL) == O_EXCL); + + req.GetFH(); + + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + Attribute attr[] = { FATTR4_FILEID }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + } + + result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, state, &sequence)) + continue; + + reply.PutFH(); + + result = reply.Open(state->fStateID, &state->fStateSeq, &confirm, + delegation, changeInfo); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + reply.GetFH(handle); + + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + *fileID = values[0].fData.fValue64; + + delete[] values; + } else + *fileID = fFileSystem->AllocFileId(); + + break; + } while (true); + + state->fOpened = true; + + if (confirm) + result = ConfirmOpen(*handle, state, &sequence); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; +} + + +status_t +NFS4Inode::OpenFile(OpenState* state, int mode, OpenDelegationData* delegation) +{ + ASSERT(state != NULL); + ASSERT(delegation != NULL); + + bool confirm; + status_t result; + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + state->fClientID = fFileSystem->NFSServer()->ClientId(); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + // Since we are opening the file using a pair (parentFH, name) we + // need to check for race conditions. + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + req.PutFH(fInfo.fParent); + req.LookUp(fInfo.fName); + AttrValue attr; + attr.fAttribute = FATTR4_FILEID; + attr.fFreePointer = false; + attr.fData.fValue64 = fInfo.fFileId; + req.Verify(&attr, 1); + } else if (fFileSystem->ExpireType() == FH4_PERSISTENT) { + req.PutFH(fInfo.fParent); + req.LookUp(fInfo.fName); + AttrValue attr; + attr.fAttribute = FATTR4_FILEHANDLE; + attr.fFreePointer = true; + attr.fData.fPointer = malloc(sizeof(fInfo.fHandle)); + memcpy(attr.fData.fPointer, &fInfo.fHandle, sizeof(fInfo.fHandle)); + req.Verify(&attr, 1); + } + + req.PutFH(fInfo.fParent); + req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID, + OPEN4_NOCREATE, fFileSystem->OpenOwner(), fInfo.fName); + req.GetFH(); + + result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, state, &sequence)) + continue; + + // Verify if the file we want to open is the file this Inode + // represents. + if (fFileSystem->IsAttrSupported(FATTR4_FILEID) + || fFileSystem->ExpireType() == FH4_PERSISTENT) { + reply.PutFH(); + result = reply.LookUp(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + result = reply.Verify(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + if (result != B_OK && reply.NFS4Error() == NFS4ERR_NOT_SAME) + return B_ENTRY_NOT_FOUND; + else if (result != B_OK) + return result; + } + + reply.PutFH(); + result = reply.Open(state->fStateID, &state->fStateSeq, &confirm, + delegation); + + FileHandle handle; + reply.GetFH(&handle); + + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + break; + } while (true); + + state->fOpened = true; + + if (confirm) + result = ConfirmOpen(fInfo.fHandle, state, &sequence); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; +} + + +status_t +NFS4Inode::OpenAttr(OpenState* state, const char* name, int mode, + OpenDelegationData* delegation, bool create) +{ + ASSERT(name != NULL); + ASSERT(state != NULL); + ASSERT(delegation != NULL); + + bool confirm; + status_t result; + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + state->fClientID = fFileSystem->NFSServer()->ClientId(); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fAttrDir); + req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID, + create ? OPEN4_CREATE : OPEN4_NOCREATE, fFileSystem->OpenOwner(), + name); + req.GetFH(); + + result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, state, &sequence)) + continue; + + reply.PutFH(); + result = reply.Open(state->fStateID, &state->fStateSeq, &confirm, + delegation); + + reply.GetFH(&state->fInfo.fHandle); + + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + break; + } while (true); + + state->fOpened = true; + + if (confirm) + result = ConfirmOpen(state->fInfo.fHandle, state, &sequence); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; +} + + +status_t +NFS4Inode::ReadFile(OpenStateCookie* cookie, OpenState* state, uint64 position, + uint32* length, void* buffer, bool* eof) +{ + ASSERT(state != NULL); + ASSERT(length != NULL); + ASSERT(buffer != NULL); + ASSERT(eof != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(state->fInfo.fHandle); + req.Read(state->fStateID, state->fStateSeq, position, *length); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv, cookie, state)) + continue; + + reply.PutFH(); + result = reply.Read(buffer, length, eof); + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::WriteFile(OpenStateCookie* cookie, OpenState* state, uint64 position, + uint32* length, const void* buffer, bool commit) +{ + ASSERT(state != NULL); + ASSERT(length != NULL); + ASSERT(buffer != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(state->fInfo.fHandle); + + req.Write(state->fStateID, state->fStateSeq, buffer, position, *length, + commit); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv, cookie, state)) + continue; + + reply.PutFH(); + + result = reply.Write(length); + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::CreateObject(const char* name, const char* path, int mode, + FileType type, ChangeInfo* changeInfo, uint64* fileID, FileHandle* handle, + bool parent) +{ + ASSERT(name != NULL); + ASSERT(changeInfo != NULL); + ASSERT(handle != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (parent) + req.PutFH(fInfo.fParent); + else + req.PutFH(fInfo.fHandle); + + uint32 i = 0; + AttrValue cattr[1]; + cattr[i].fAttribute = FATTR4_MODE; + cattr[i].fFreePointer = false; + cattr[i].fData.fValue32 = mode; + i++; + + switch (type) { + case NF4DIR: + req.Create(NF4DIR, name, cattr, i); + break; + case NF4LNK: + req.Create(NF4LNK, name, cattr, i, path); + break; + default: + return B_BAD_VALUE; + } + + req.GetFH(); + + if (fileID != NULL) { + 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.Create(&changeInfo->fBefore, &changeInfo->fAfter, + changeInfo->fAtomic); + if (result != B_OK) + return result; + + result = reply.GetFH(handle); + if (result != B_OK) + return result; + + if (fileID != NULL) { + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + if (count == 0) + *fileID = fFileSystem->AllocFileId(); + else + *fileID = values[0].fData.fValue64; + + delete[] values; + } + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::RemoveObject(const char* name, FileType type, ChangeInfo* changeInfo, + uint64* fileID) +{ + ASSERT(name != NULL); + ASSERT(changeInfo != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.LookUp(name); + AttrValue attr; + attr.fAttribute = FATTR4_TYPE; + attr.fFreePointer = false; + attr.fData.fValue32 = NF4DIR; + if (type == NF4DIR) + req.Verify(&attr, 1); + else + req.Nverify(&attr, 1); + + if (type != NF4NAMEDATTR) { + Attribute idAttr[] = { FATTR4_FILEID }; + req.GetAttr(idAttr, sizeof(idAttr) / sizeof(Attribute)); + } + + req.PutFH(fInfo.fHandle); + req.Remove(name); + + 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.LookUp(); + if (result != B_OK) + return result; + + if (type == NF4DIR) + result = reply.Verify(); + else + result = reply.Nverify(); + + if (result == NFS4ERR_SAME && type != NF4DIR) + return B_IS_A_DIRECTORY; + if (result == NFS4ERR_NOT_SAME && type == NF4DIR) + return B_NOT_A_DIRECTORY; + if (result != B_OK) + return result; + + if (type != NF4NAMEDATTR) { + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + if (count == 0) + *fileID = fFileSystem->AllocFileId(); + else + *fileID = values[0].fData.fValue64; + delete[] values; + } + + reply.PutFH(); + return reply.Remove(&changeInfo->fBefore, &changeInfo->fAfter, + changeInfo->fAtomic); + } while (true); +} + + +status_t +NFS4Inode::ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie, + bool* eof, uint64* change, uint64* dirCookie, uint64* dirCookieVerf, + bool attribute) +{ + ASSERT(dirents != NULL); + ASSERT(count != NULL); + ASSERT(eof != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (attribute) + req.PutFH(fInfo.fAttrDir); + else + req.PutFH(fInfo.fHandle); + + Attribute dirAttr[] = { FATTR4_CHANGE }; + if (*change == 0) + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + + Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID }; + req.ReadDir(*count, *dirCookie, *dirCookieVerf, attr, + sizeof(attr) / sizeof(Attribute)); + + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + AttrValue* before = NULL; + uint32 attrCount; + if (*change == 0) { + result = reply.GetAttr(&before, &attrCount); + if (result != B_OK) + return result; + } + + result = reply.ReadDir(dirCookie, dirCookieVerf, dirents, count, eof); + if (result != B_OK) { + delete[] before; + return result; + } + + AttrValue* after; + result = reply.GetAttr(&after, &attrCount); + if (result != B_OK) { + delete[] before; + return result; + } + + if ((*change == 0 + && before[0].fData.fValue64 == after[0].fData.fValue64) + || *change == after[0].fData.fValue64) + *change = after[0].fData.fValue64; + else + return B_ERROR; + + delete[] before; + delete[] after; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::OpenAttrDir(FileHandle* handle) +{ + ASSERT(handle != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.OpenAttrDir(true); + req.GetFH(); + + 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.OpenAttrDir(); + if (result != B_OK) + return result; + + return reply.GetFH(handle); + } while (true); +} + + +status_t +NFS4Inode::TestLock(OpenFileCookie* cookie, LockType* type, uint64* position, + uint64* length, bool& conflict) +{ + ASSERT(cookie != NULL); + ASSERT(type != NULL); + ASSERT(position != NULL); + ASSERT(length != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.LockT(*type, *position, *length, cookie->fOpenState); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + if (HandleErrors(reply.NFS4Error(), serv, cookie)) + continue; + + reply.PutFH(); + result = reply.LockT(position, length, type); + if (reply.NFS4Error() == NFS4ERR_DENIED) { + conflict = true; + result = B_OK; + } else if (reply.NFS4Error() == NFS4_OK) + conflict = false; + + return result; + } while (true); + + return B_OK; +} + + +status_t +NFS4Inode::AcquireLock(OpenFileCookie* cookie, LockInfo* lockInfo, bool wait) +{ + ASSERT(cookie != NULL); + ASSERT(lockInfo != NULL); + + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + MutexLocker ownerLocker(lockInfo->fOwner->fLock); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Lock(cookie->fOpenState, lockInfo, &sequence); + + status_t result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + reply.PutFH(); + result = reply.Lock(lockInfo); + + ownerLocker.Unlock(); + if (wait && reply.NFS4Error() == NFS4ERR_DENIED) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + snooze_etc(sSecToBigTime(5), B_SYSTEM_TIMEBASE, + B_RELATIVE_TIMEOUT); + sequence = fFileSystem->OpenOwnerSequenceLock(); + continue; + } + if (HandleErrors(reply.NFS4Error(), serv, cookie, NULL, &sequence)) + continue; + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::ReleaseLock(OpenFileCookie* cookie, LockInfo* lockInfo) +{ + ASSERT(cookie != NULL); + ASSERT(lockInfo != NULL); + + do { + MutexLocker ownerLocker(lockInfo->fOwner->fLock); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.LockU(lockInfo); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + reply.PutFH(); + result = reply.LockU(lockInfo); + + ownerLocker.Unlock(); + if (HandleErrors(reply.NFS4Error(), serv, cookie)) + continue; + + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h new file mode 100644 index 0000000000..4aff6886c9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h @@ -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 + +#include + +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp new file mode 100644 index 0000000000..69381930f9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp @@ -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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Object.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.h new file mode 100644 index 0000000000..d0ac9cb83a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.h @@ -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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp new file mode 100644 index 0000000000..e92ede74ba --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp @@ -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::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(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& list = fs->DelegationsLock(); + DoublyLinkedList::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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Server.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.h new file mode 100644 index 0000000000..624b0fe26a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.h @@ -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 + +#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 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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/OpenState.cpp b/src/add-ons/kernel/file_systems/nfs4/OpenState.cpp new file mode 100644 index 0000000000..21ac8d5b78 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/OpenState.cpp @@ -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 + +#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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/OpenState.h b/src/add-ons/kernel/file_systems/nfs4/OpenState.h new file mode 100644 index 0000000000..f810528903 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/OpenState.h @@ -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 +#include +#include + +#include "Cookie.h" +#include "NFS4Object.h" + + +struct OpenState : public NFS4Object, public KernelReferenceable, + public DoublyLinkedListLinkImpl { + 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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCAuth.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.cpp new file mode 100644 index 0000000000..b10f2abba0 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.cpp @@ -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 +#include +#include + +#include +#include + +#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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCAuth.h b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.h new file mode 100644 index 0000000000..d45a7cf460 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.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 + */ +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCall.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCall.cpp new file mode 100644 index 0000000000..d0c009945a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCall.cpp @@ -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 +#include + +#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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCall.h b/src/add-ons/kernel/file_systems/nfs4/RPCCall.h new file mode 100644 index 0000000000..f1739bd4d1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCall.h @@ -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 diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallback.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.cpp new file mode 100644 index 0000000000..baf6246759 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.cpp @@ -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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallback.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.h new file mode 100644 index 0000000000..9f579be9cf --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.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 + */ +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.cpp new file mode 100644 index 0000000000..595bb0989f --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.cpp @@ -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 + +#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() +{ +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.h new file mode 100644 index 0000000000..485d5fa8a8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.h @@ -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 diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.cpp new file mode 100644 index 0000000000..54b9ef7b6f --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.cpp @@ -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 + +#include + +#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); +} + + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.h new file mode 100644 index 0000000000..19e2e006c5 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.h @@ -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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.cpp new file mode 100644 index 0000000000..c296289d84 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.cpp @@ -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(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(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(object); + CallbackServer* server = reinterpret_cast(objects[0]); + ConnectionEntry* entry = reinterpret_cast(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(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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.h new file mode 100644 index 0000000000..fce7354b02 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.h @@ -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 + +#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(id) < fArraySize) + return fCallbackArray[id].fCallback; + return NULL; +} + + +} // namespace RPC + +#endif // RPCCALLBACKSERVER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCDefs.h b/src/add-ons/kernel/file_systems/nfs4/RPCDefs.h new file mode 100644 index 0000000000..3363eaeb66 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCDefs.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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCReply.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCReply.cpp new file mode 100644 index 0000000000..bca2eaa5bf --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCReply.cpp @@ -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 +#include + +#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); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCReply.h b/src/add-ons/kernel/file_systems/nfs4/RPCReply.h new file mode 100644 index 0000000000..50a200b3b8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCReply.h @@ -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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp new file mode 100644 index 0000000000..811d3d4a5a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp @@ -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 + +#include + +#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(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(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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCServer.h b/src/add-ons/kernel/file_systems/nfs4/RPCServer.h new file mode 100644 index 0000000000..35c52f8bc1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCServer.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 + */ +#ifndef RPCSERVER_H +#define RPCSERVER_H + + +#include +#include + +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.cpp new file mode 100644 index 0000000000..152d5de25b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.cpp @@ -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; + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.h b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.h new file mode 100644 index 0000000000..2a12a0d522 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.h @@ -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 + +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp new file mode 100644 index 0000000000..271cabb490 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp @@ -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 + +#include + +#include "Cookie.h" + + +FSLocation::~FSLocation() +{ + free(const_cast(fRootPath)); + for (uint32 i = 0; i < fCount; i++) + free(const_cast(fLocations[i])); + delete[] fLocations; +} + + +FSLocations::~FSLocations() +{ + free(const_cast(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(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(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(malloc(strlen(path + 1))); + pathname[0] = '\0'; + } else { + *pathname++ = '/'; + pathname = reinterpret_cast(realloc(pathname, size)); + } + strcat(pathname, path); + free(const_cast(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(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(stream.GetHyper()); + ts.tv_nsec = static_cast(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(stream.GetHyper()); + ts.tv_nsec = static_cast(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(stream.GetHyper()); + ts.tv_nsec = static_cast(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(stream.GetHyper()); + ts.tv_nsec = static_cast(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; + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h new file mode 100644 index 0000000000..b630b8ffb9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h @@ -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 + +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/Request.cpp b/src/add-ons/kernel/file_systems/nfs4/Request.cpp new file mode 100644 index 0000000000..b8e8f9663d --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Request.cpp @@ -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(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Request.h b/src/add-ons/kernel/file_systems/nfs4/Request.h new file mode 100644 index 0000000000..f740bb4619 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Request.h @@ -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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp new file mode 100644 index 0000000000..83ff3b4cf8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp @@ -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 +#include + +#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(malloc(sizeof(Attribute) * count)); + for (uint32 i = 0; i < count; i++) + attrs[i] = static_cast(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(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(attr[i].fData.fPointer)); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_OWNER_GROUP) { + str.AddString(reinterpret_cast(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(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(attr[i].fData.fPointer); + str.AddHyper(ts->tv_sec); + str.AddUInt(ts->tv_nsec); + + i++; + } + + stream.AddOpaque(str); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h new file mode 100644 index 0000000000..6bf75248f9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h @@ -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 + +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.cpp new file mode 100644 index 0000000000..11ca0a235b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.cpp @@ -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 + +#include + + +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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.h b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.h new file mode 100644 index 0000000000..a17f2cebd7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.h @@ -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 + +#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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/RootInode.cpp b/src/add-ons/kernel/file_systems/nfs4/RootInode.cpp new file mode 100644 index 0000000000..a215d6bb7b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RootInode.cpp @@ -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 + +#include "MetadataCache.h" +#include "Request.h" + + +RootInode::RootInode() + : + fInfoCacheExpire(0), + fName(NULL), + fIOSize(0) +{ + mutex_init(&fInfoCacheLock, NULL); +} + + +RootInode::~RootInode() +{ + free(const_cast(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; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RootInode.h b/src/add-ons/kernel/file_systems/nfs4/RootInode.h new file mode 100644 index 0000000000..8fff6adc1a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RootInode.h @@ -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 + +#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(fName)); + fName = strdup(name); +} + + +#endif // ROOTINODE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp new file mode 100644 index 0000000000..b77feae8f4 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp @@ -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); + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h new file mode 100644 index 0000000000..302457a721 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h @@ -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 +#include +#include + +#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 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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp new file mode 100644 index 0000000000..da435bfb43 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp @@ -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 + + +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(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(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(args)); + break; + case IORequest: + JobIO(reinterpret_cast(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(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(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/WorkQueue.h b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.h new file mode 100644 index 0000000000..954d011388 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.h @@ -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 +#include +#include +#include + +#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 { + 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 fQueue; + + sem_id fThreadCancel; + thread_id fThread; +}; + + +inline status_t +WorkQueue::InitStatus() +{ + return fInitError; +} + + +extern WorkQueue* gWorkQueue; + + +#endif // WORKQUEUE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/XDR.cpp b/src/add-ons/kernel/file_systems/nfs4/XDR.cpp new file mode 100644 index 0000000000..86a6462525 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/XDR.cpp @@ -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 +#include + +#include + + +using namespace XDR; + +Stream::Stream(void* buffer, uint32 size) + : + fBuffer(reinterpret_cast(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(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(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(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(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(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(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(ptr); + fSize = new_size; + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/XDR.h b/src/add-ons/kernel/file_systems/nfs4/XDR.h new file mode 100644 index 0000000000..78cfbc6392 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/XDR.h @@ -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 + + +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(x)); +} + + +inline status_t +WriteStream::Error() const +{ + return fError; +} + + +} // namespace XDR + + +#endif // XDR_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.cpp b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.cpp new file mode 100644 index 0000000000..2622663a9f --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.cpp @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +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(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(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(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(name)); + } else { + result = write_port(gReplyPort, MsgReply, kNobodyName, + strlen(kNobodyName) + 1); + } + + return result; +} + + +status_t +NameToGID(void* buffer) +{ + char* groupName = reinterpret_cast(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(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(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(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(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.h b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.h new file mode 100644 index 0000000000..bf0707ea1e --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.h @@ -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 + diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/Jamfile b/src/add-ons/kernel/file_systems/nfs4/idmapper/Jamfile new file mode 100644 index 0000000000..f9f4bb2f20 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/Jamfile @@ -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 +; diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/nfs4_idmapper_server.rdef b/src/add-ons/kernel/file_systems/nfs4/idmapper/nfs4_idmapper_server.rdef new file mode 100644 index 0000000000..9b601815d2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/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" +}; + diff --git a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp new file mode 100644 index 0000000000..b3366cf4fc --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp @@ -0,0 +1,1528 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#define __STDC_FORMAT_MACROS +#include +#include + +#include +#include +#include + +#include "Connection.h" +#include "FileSystem.h" +#include "IdMap.h" +#include "Inode.h" +#include "NFS4Defs.h" +#include "RequestBuilder.h" +#include "ReplyInterpreter.h" +#include "RootInode.h" +#include "RPCCallbackServer.h" +#include "RPCServer.h" +#include "VnodeToInode.h" +#include "WorkQueue.h" + +#ifdef DEBUG +#define TRACE_NFS4 +#endif + +#ifdef TRACE_NFS4 +static mutex gTraceLock = MUTEX_INITIALIZER(NULL); + +#define TRACE(x...) \ + { \ + mutex_lock(&gTraceLock); \ + dprintf("nfs4: %s(): ", __FUNCTION__); \ + dprintf(x); \ + dprintf("\n"); \ + mutex_unlock(&gTraceLock); \ + } +#else +#define TRACE(x...) (void)0 +#endif + +extern fs_volume_ops gNFSv4VolumeOps; +extern fs_vnode_ops gNFSv4VnodeOps; + + +RPC::ServerManager* gRPCServerManager; + + +RPC::ProgramData* +CreateNFS4Server(RPC::Server* serv) +{ + return new NFS4Server(serv); +} + + +// Format: ip{4,6}_address:path options +// Available options: +// hard - retry requests until success +// soft - retry requests no more than retrans times (default) +// timeo=X - request time limit before next retransmission (default: 60s) +// retrans=X - retry requests X times (default: 5) +// ac - use metadata cache (default) +// noac - do not use metadata cache +// xattr-emu - emulate named attributes +// noxattr-emu - do not emulate named attributes (default) +// port=X - connect to port X (default: 2049) +// proto=X - user transport protocol X (default: tcp) +static status_t +ParseArguments(const char* _args, AddressResolver** address, char** _path, + MountConfiguration* conf) +{ + if (_args == NULL) + return B_BAD_VALUE; + + char* args = strdup(_args); + if (args == NULL) + return B_NO_MEMORY; + MemoryDeleter argsDeleter(args); + + char* options = strchr(args, ' '); + if (options != NULL) + *options++ = '\0'; + + char* path = strrchr(args, ':'); + if (path == NULL) + return B_MISMATCHED_VALUES; + *path++ = '\0'; + + *address = new AddressResolver(args); + if (*address == NULL) + return B_NO_MEMORY; + + *_path = strdup(path); + if (*_path == NULL) { + delete *address; + return B_NO_MEMORY; + } + + conf->fHard = false; + conf->fRetryLimit = 5; + conf->fRequestTimeout = sSecToBigTime(60); + conf->fEmulateNamedAttrs = false; + conf->fCacheMetadata = true; + + char* optionsEnd; + if (options != NULL) + optionsEnd = strchr(options, ' '); + while (options != NULL && *options != '\0') { + if (optionsEnd != NULL) + *optionsEnd++ = '\0'; + + if (strcmp(options, "hard") == 0) + conf->fHard = true; + else if (strncmp(options, "retrans=", 8) == 0) { + options += strlen("retrans="); + conf->fRetryLimit = atoi(options); + } else if (strncmp(options, "timeo=", 6) == 0) { + options += strlen("timeo="); + conf->fRequestTimeout = atoi(options); + } else if (strcmp(options, "noac") == 0) + conf->fCacheMetadata = false; + else if (strcmp(options, "xattr-emu") == 0) + conf->fEmulateNamedAttrs = true; + else if (strncmp(options, "port=", 5) == 0) { + options += strlen("port="); + (*address)->ForcePort(atoi(options)); + } else if (strncmp(options, "proto=", 6) == 0) { + options += strlen("proto="); + (*address)->ForceProtocol(options); + } + + options = optionsEnd; + if (options != NULL) + optionsEnd = strchr(options, ' '); + } + + return B_OK; +} + + +static status_t +nfs4_mount(fs_volume* volume, const char* device, uint32 flags, + const char* args, ino_t* _rootVnodeID) +{ + TRACE("volume = %p, device = %s, flags = %" PRIu32 ", args = %s", volume, + device, flags, args); + + status_t result; + + /* prepare idmapper server */ + MutexLocker locker(gIdMapperLock); + gIdMapper = new(std::nothrow) IdMap; + if (gIdMapper == NULL) + return B_NO_MEMORY; + + result = gIdMapper->InitStatus(); + if (result != B_OK) { + delete gIdMapper; + gIdMapper = NULL; + return result; + } + locker.Unlock(); + + AddressResolver* resolver; + MountConfiguration config; + char* path; + result = ParseArguments(args, &resolver, &path, &config); + if (result != B_OK) + return result; + MemoryDeleter pathDeleter(path); + + RPC::Server* server; + result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server); + delete resolver; + if (result != B_OK) + return result; + + FileSystem* fs; + result = FileSystem::Mount(&fs, server, path, volume->id, config); + if (result != B_OK) { + gRPCServerManager->Release(server); + return result; + } + + Inode* inode = fs->Root(); + if (inode == NULL) { + delete fs; + gRPCServerManager->Release(server); + + return B_IO_ERROR; + } + + volume->private_volume = fs; + volume->ops = &gNFSv4VolumeOps; + + VnodeToInode* vti = new VnodeToInode(inode->ID(), fs); + if (vti == NULL) { + delete fs; + gRPCServerManager->Release(server); + return B_NO_MEMORY; + } + + vti->Replace(inode); + result = publish_vnode(volume, inode->ID(), vti, &gNFSv4VnodeOps, + inode->Type(), 0); + if (result != B_OK) + return result; + + *_rootVnodeID = inode->ID(); + + TRACE("*_rootVnodeID = %" PRIi64, inode->ID()); + + return B_OK; +} + + +static status_t +nfs4_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type, + uint32* _flags, bool reenter) +{ + FileSystem* fs = reinterpret_cast(volume->private_volume); + TRACE("volume = %p, id = %" PRIi64, volume, id); + + VnodeToInode* vnodeToInode = new VnodeToInode(id, fs); + if (vnodeToInode == NULL) + return B_NO_MEMORY; + + Inode* inode; + status_t result = fs->GetInode(id, &inode); + if (result != B_OK) + return result; + + vnodeToInode->Replace(inode); + vnode->ops = &gNFSv4VnodeOps; + vnode->private_node = vnodeToInode; + + *_type = inode->Type(); + *_flags = 0; + + return B_OK; +} + + +static status_t +nfs4_unmount(fs_volume* volume) +{ + TRACE("volume = %p", volume); + FileSystem* fs = reinterpret_cast(volume->private_volume); + RPC::Server* server = fs->Server(); + + delete fs; + gRPCServerManager->Release(server); + + return B_OK; +} + + +static status_t +nfs4_read_fs_info(fs_volume* volume, struct fs_info* info) +{ + TRACE("volume = %p", volume); + + FileSystem* fs = reinterpret_cast(volume->private_volume); + RootInode* inode = reinterpret_cast(fs->Root()); + return inode->ReadInfo(info); +} + + +static status_t +nfs4_lookup(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id) +{ + VnodeToInode* vti = reinterpret_cast(dir->private_node); + + if (!strcmp(name, ".")) { + *_id = vti->ID(); + void* ptr; + return get_vnode(volume, *_id, &ptr); + } + + VnodeToInodeLocker locker(vti); + + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + TRACE("volume = %p, dir = %" PRIi64 ", name = %s", volume, vti->ID(), name); + + status_t result = inode->LookUp(name, _id); + if (result != B_OK) + return result; + locker.Unlock(); + + TRACE("*_id = %" PRIi64, *_id); + + // If VTI holds an outdated Inode next operation performed on it will + // return either ERR_STALE or ERR_FHEXPIRED. Both of these error codes + // will cause FileInfo data to be updated (the former will also cause Inode + // object to be recreated). We are taking an optimistic (an lazy) approach + // here. The following code just ensures VTI won't be removed too soon. + void* ptr; + result = get_vnode(volume, *_id, &ptr); + if (result == B_OK) + unremove_vnode(volume, *_id); + + return result; +} + + +static status_t +nfs4_get_vnode_name(fs_volume* volume, fs_vnode* vnode, char* buffer, + size_t bufferSize) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + strncpy(buffer, inode->Name(), bufferSize); + return B_OK; +} + + +static status_t +nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64, volume, vti->ID()); + + delete vti; + return B_OK; +} + + +static status_t +nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) +{ + FileSystem* fs = reinterpret_cast(volume->private_volume); + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64, volume, vti->ID()); + + if (fs->Root() == vti->GetPointer()) + return B_OK; + + ASSERT(vti->GetPointer() == NULL); + delete vti; + + return B_OK; +} + + +static status_t +nfs4_read_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos, + const iovec* vecs, size_t count, size_t* _numBytes) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, pos = %" PRIi64 \ + ", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos, + count, *_numBytes); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + status_t result; + size_t totalRead = 0; + bool eof = false; + for (size_t i = 0; i < count && !eof; i++) { + size_t bytesLeft = vecs[i].iov_len; + char* buffer = reinterpret_cast(vecs[i].iov_base); + + do { + size_t bytesRead = bytesLeft; + result = inode->ReadDirect(cookie, pos, buffer, &bytesRead, &eof); + if (result != B_OK) + return result; + + totalRead += bytesRead; + pos += bytesRead; + buffer += bytesRead; + bytesLeft -= bytesRead; + } while (bytesLeft > 0 && !eof); + } + + *_numBytes = totalRead; + + TRACE("*numBytes = %lu", totalRead); + + return B_OK; +} + + +static status_t +nfs4_write_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos, + const iovec* vecs, size_t count, size_t* _numBytes) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, pos = %" PRIi64 \ + ", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos, + count, *_numBytes); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + status_t result; + for (size_t i = 0; i < count; i++) { + uint64 bytesLeft = vecs[i].iov_len; + if (pos + bytesLeft > inode->MaxFileSize()) + bytesLeft = inode->MaxFileSize() - pos; + + char* buffer = reinterpret_cast(vecs[i].iov_base); + + do { + size_t bytesWritten = bytesLeft; + + result = inode->WriteDirect(cookie, pos, buffer, &bytesWritten); + if (result != B_OK) + return result; + + bytesLeft -= bytesWritten; + pos += bytesWritten; + buffer += bytesWritten; + } while (bytesLeft > 0); + } + + return B_OK; +} + + +static status_t +nfs4_io(fs_volume* volume, fs_vnode* vnode, void* cookie, io_request* request) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p", volume, vti->ID(), + cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + IORequestArgs* args = new(std::nothrow) IORequestArgs; + if (args == NULL) { + notify_io_request(request, B_NO_MEMORY); + return B_NO_MEMORY; + } + args->fRequest = request; + args->fInode = inode; + + status_t result = gWorkQueue->EnqueueJob(IORequest, args); + if (result != B_OK) + notify_io_request(request, result); + + return result; +} + + +static status_t +nfs4_get_file_map(fs_volume* volume, fs_vnode* vnode, off_t _offset, + size_t size, struct file_io_vec* vecs, size_t* _count) +{ + return B_ERROR; +} + + +static status_t +nfs4_set_flags(fs_volume* volume, fs_vnode* vnode, void* _cookie, int flags) +{ + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, flags = %d", volume, + reinterpret_cast(vnode->private_node)->ID(), _cookie, + flags); + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + cookie->fMode = (cookie->fMode & ~(O_APPEND | O_NONBLOCK)) | flags; + return B_OK; +} + + +static status_t +nfs4_fsync(fs_volume* volume, fs_vnode* vnode) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->SyncAndCommit(); +} + + +static status_t +nfs4_read_symlink(fs_volume* volume, fs_vnode* link, char* buffer, + size_t* _bufferSize) +{ + VnodeToInode* vti = reinterpret_cast(link->private_node); + TRACE("volume = %p, link = %" PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->ReadLink(buffer, _bufferSize); +} + + +static status_t +nfs4_create_symlink(fs_volume* volume, fs_vnode* dir, const char* name, + const char* path, int mode) +{ + VnodeToInode* vti = reinterpret_cast(dir->private_node); + TRACE("volume = %p, dir = %" PRIi64 ", name = %s, path = %s, mode = %d", + volume, vti->ID(), name, path, mode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = inode->CreateLink(name, path, mode, &id); + if (result != B_OK) + return result; + + result = get_vnode(volume, id, reinterpret_cast(&vti)); + if (result == B_OK) { + unremove_vnode(volume, id); + vti->Clear(); + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* vnode) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + VnodeToInode* dirVti = reinterpret_cast(dir->private_node); + TRACE("volume = %p, dir = %" PRIi64 ", name = %s, vnode = %" PRIi64, volume, + dirVti->ID(), name, vti->ID()); + + VnodeToInodeLocker _dir(dirVti); + Inode* dirInode = dirVti->Get(); + if (dirInode == NULL) + return B_ENTRY_NOT_FOUND; + + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Link(dirInode, name); +} + + +static status_t +nfs4_unlink(fs_volume* volume, fs_vnode* dir, const char* name) +{ + VnodeToInode* vti = reinterpret_cast(dir->private_node); + + VnodeToInodeLocker locker(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + TRACE("volume = %p, dir = %" PRIi64 ", name = %s", volume, vti->ID(), name); + + ino_t id; + status_t result = inode->Remove(name, NF4REG, &id); + if (result != B_OK) + return result; + locker.Unlock(); + + result = acquire_vnode(volume, id); + if (result == B_OK) { + ASSERT(get_vnode(volume, id, reinterpret_cast(&vti)) == B_OK); + vti->Remove(); + put_vnode(volume, id); + remove_vnode(volume, id); + + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName, + fs_vnode* toDir, const char* toName) +{ + VnodeToInode* fromVti + = reinterpret_cast(fromDir->private_node); + VnodeToInode* toVti = reinterpret_cast(toDir->private_node); + TRACE("volume = %p, fromDir = %" PRIi64 ", toDir = %" PRIi64 "," \ + " fromName = %s, toName = %s", volume, fromVti->ID(), toVti->ID(), \ + fromName, toName); + + VnodeToInodeLocker _from(fromVti); + Inode* fromInode = fromVti->Get(); + if (fromInode == NULL) + return B_ENTRY_NOT_FOUND; + + + VnodeToInodeLocker _to(toVti); + Inode* toInode = toVti->Get(); + if (toInode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = Inode::Rename(fromInode, toInode, fromName, toName, false, + &id); + if (result != B_OK) + return result; + + VnodeToInode* vti; + result = get_vnode(volume, id, reinterpret_cast(&vti)); + if (result == B_OK) { + Inode* child = vti->Get(); + if (child == NULL) { + put_vnode(volume, id); + return B_ENTRY_NOT_FOUND; + } + + unremove_vnode(volume, id); + child->fInfo.fParent = toInode->fInfo.fHandle; + child->fInfo.CreateName(toInode->fInfo.fPath, toName); + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_access(fs_volume* volume, fs_vnode* vnode, int mode) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", mode = %d", volume, vti->ID(), + mode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Access(mode); +} + + +static status_t +nfs4_read_stat(fs_volume* volume, fs_vnode* vnode, struct stat* stat) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Stat(stat); +} + + +static status_t +nfs4_write_stat(fs_volume* volume, fs_vnode* vnode, const struct stat* stat, + uint32 statMask) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", statMask = %" PRIu32, volume, + vti->ID(), statMask); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->WriteStat(stat, statMask); +} + + +static status_t +get_new_vnode(fs_volume* volume, ino_t id, VnodeToInode** _vti) +{ + FileSystem* fs = reinterpret_cast(volume->private_volume); + Inode* inode; + VnodeToInode* vti; + + status_t result = acquire_vnode(volume, id); + if (result == B_OK) { + ASSERT(get_vnode(volume, id, reinterpret_cast(_vti)) == B_OK); + unremove_vnode(volume, id); + + // Release after acquire + put_vnode(volume, id); + + vti = *_vti; + + if (vti->Get() == NULL) { + result = fs->GetInode(id, &inode); + if (result != B_OK) { + put_vnode(volume, id); + return result; + } + + vti->Replace(inode); + } + return B_OK; + } + + return get_vnode(volume, id, reinterpret_cast(_vti)); +} + + +static status_t +nfs4_create(fs_volume* volume, fs_vnode* dir, const char* name, int openMode, + int perms, void** _cookie, ino_t* _newVnodeID) +{ + OpenFileCookie* cookie = new OpenFileCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + VnodeToInode* vti = reinterpret_cast(dir->private_node); + TRACE("volume = %p, dir = %" PRIi64 ", name = %s, openMode = %d," \ + " perms = %d", volume, vti->ID(), name, openMode, perms); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + FileSystem* fs = reinterpret_cast(volume->private_volume); + MutexLocker createLocker(fs->CreateFileLock()); + + OpenDelegationData data; + status_t result = inode->Create(name, openMode, perms, cookie, &data, + _newVnodeID); + if (result != B_OK) { + delete cookie; + return result; + } + + result = get_new_vnode(volume, *_newVnodeID, &vti); + if (result != B_OK) { + delete cookie; + return result; + } + + VnodeToInodeLocker _child(vti); + Inode* child = vti->Get(); + if (child == NULL) { + delete cookie; + put_vnode(volume, *_newVnodeID); + return B_ENTRY_NOT_FOUND; + } + + child->SetOpenState(cookie->fOpenState); + + if (data.fType != OPEN_DELEGATE_NONE) { + Delegation* delegation + = new(std::nothrow) Delegation(data, child, + cookie->fOpenState->fClientID); + if (delegation != NULL) { + delegation->fInfo = cookie->fOpenState->fInfo; + delegation->fFileSystem = child->GetFileSystem(); + child->SetDelegation(delegation); + } + } + + TRACE("*cookie = %p, *newVnodeID = %" PRIi64, *_cookie, *_newVnodeID); + return result; +} + + +static status_t +nfs4_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", openMode = %d", volume, vti->ID(), + openMode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) { + *_cookie = NULL; + return B_OK; + } + + OpenFileCookie* cookie = new OpenFileCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + status_t result = inode->Open(openMode, cookie); + if (result != B_OK) + delete cookie; + + TRACE("*cookie = %p", *_cookie); + + return result; +} + + +static status_t +nfs4_close(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p", volume, vti->ID(), + _cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) + return B_OK; + + Cookie* cookie = reinterpret_cast(_cookie); + return cookie->CancelAll(); +} + + +static status_t +nfs4_free_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p", volume, vti->ID(), + _cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) + return B_OK; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + inode->Close(cookie); + delete cookie; + + return B_OK; +} + + +static status_t +nfs4_read(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + void* buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, pos = %" PRIi64 \ + ", length = %lu", volume, vti->ID(), _cookie, pos, *length); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR) + return B_IS_A_DIRECTORY; + + if (inode->Type() == S_IFLNK) + return B_BAD_VALUE; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + return inode->Read(cookie, pos, buffer, length);; +} + + +static status_t +nfs4_write(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + const void* _buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, pos = %" PRIi64 \ + ", length = %lu", volume, vti->ID(), _cookie, pos, *length); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR) + return B_IS_A_DIRECTORY; + + if (inode->Type() == S_IFLNK) + return B_BAD_VALUE; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + return inode->Write(cookie, pos, _buffer, length); +} + + +static status_t +nfs4_create_dir(fs_volume* volume, fs_vnode* parent, const char* name, + int mode) +{ + VnodeToInode* vti = reinterpret_cast(parent->private_node); + TRACE("volume = %p, parent = %" PRIi64 ", mode = %d", volume, vti->ID(), + mode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = inode->CreateDir(name, mode, &id); + if (result != B_OK) + return result; + + result = get_vnode(volume, id, reinterpret_cast(&vti)); + if (result == B_OK) { + unremove_vnode(volume, id); + vti->Clear(); + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name) +{ + VnodeToInode* vti = reinterpret_cast(parent->private_node); + TRACE("volume = %p, parent = %" PRIi64 ", name = %s", volume, vti->ID(), + name); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = inode->Remove(name, NF4DIR, &id); + if (result != B_OK) + return result; + + result = acquire_vnode(volume, id); + if (result == B_OK) { + ASSERT(get_vnode(volume, id, reinterpret_cast(&vti)) == B_OK); + vti->Remove(); + put_vnode(volume, id); + remove_vnode(volume, id); + + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie) +{ + OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + status_t result = inode->OpenDir(cookie); + if (result != B_OK) + delete cookie; + + TRACE("*cookie = %p", *_cookie); + + return result; +} + + +static status_t +nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p", volume, + reinterpret_cast(vnode->private_node)->ID(), _cookie); + + Cookie* cookie = reinterpret_cast(_cookie); + return cookie->CancelAll(); +} + + +static status_t +nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p", volume, + reinterpret_cast(vnode->private_node)->ID(), cookie); + + delete reinterpret_cast(cookie); + return B_OK; +} + + +static status_t +nfs4_read_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie, + struct dirent* buffer, size_t bufferSize, uint32* _num) +{ + OpenDirCookie* cookie = reinterpret_cast(_cookie); + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p", volume, vti->ID(), + _cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->ReadDir(buffer, bufferSize, _num, cookie); +} + + +static status_t +nfs4_rewind_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p", volume, + reinterpret_cast(vnode->private_node)->ID(), _cookie); + + OpenDirCookie* cookie = reinterpret_cast(_cookie); + cookie->fSpecial = 0; + if (cookie->fSnapshot != NULL) + cookie->fSnapshot->ReleaseReference(); + cookie->fSnapshot = NULL; + cookie->fCurrent = NULL; + cookie->fEOF = false; + + return B_OK; +} + + +static status_t +nfs4_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie) +{ + OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + status_t result = inode->OpenAttrDir(cookie); + if (result != B_OK) + delete cookie; + + return result; +} + + +static status_t +nfs4_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + return nfs4_close_dir(volume, vnode, cookie); +} + + +static status_t +nfs4_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + return nfs4_free_dir_cookie(volume, vnode, cookie); +} + + +static status_t +nfs4_read_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie, + struct dirent* buffer, size_t bufferSize, uint32* _num) +{ + return nfs4_read_dir(volume, vnode, cookie, buffer, bufferSize, _num); +} + + +static status_t +nfs4_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + return nfs4_rewind_dir(volume, vnode, cookie); +} + + +static status_t +nfs4_create_attr(fs_volume* volume, fs_vnode* vnode, const char* name, + uint32 type, int openMode, void** _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenAttrCookie* cookie = new OpenAttrCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + status_t result = inode->OpenAttr(name, openMode, cookie, true, type); + if (result != B_OK) + delete cookie; + + return result; +} + + +static status_t +nfs4_open_attr(fs_volume* volume, fs_vnode* vnode, const char* name, + int openMode, void** _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenAttrCookie* cookie = new OpenAttrCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + status_t result = inode->OpenAttr(name, openMode, cookie, false); + if (result != B_OK) + delete cookie; + + return result; +} + + +static status_t +nfs4_close_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + Cookie* cookie = reinterpret_cast(_cookie); + return cookie->CancelAll(); +} + + +static status_t +nfs4_free_attr_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + inode->CloseAttr(cookie); + delete cookie; + + return B_OK; +} + + +static status_t +nfs4_read_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + void* buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + bool eof; + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->ReadDirect(cookie, pos, buffer, length, &eof); +} + + +static status_t +nfs4_write_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + const void* buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->WriteDirect(cookie, pos, buffer, length); +} + + +static status_t +nfs4_read_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie, + struct stat* stat) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Stat(stat, cookie); +} + + +static status_t +nfs4_write_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie, + const struct stat* stat, int statMask) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->WriteStat(stat, statMask, cookie); +} + + +static status_t +nfs4_rename_attr(fs_volume* volume, fs_vnode* fromVnode, const char* fromName, + fs_vnode* toVnode, const char* toName) +{ + VnodeToInode* vti = reinterpret_cast(toVnode->private_node); + VnodeToInodeLocker to(vti); + Inode* toInode = vti->Get(); + if (toInode == NULL) + return B_ENTRY_NOT_FOUND; + + vti = reinterpret_cast(fromVnode->private_node); + VnodeToInodeLocker from(vti); + Inode* fromInode = vti->Get(); + if (fromInode == NULL) + return B_ENTRY_NOT_FOUND; + + return Inode::Rename(fromInode, toInode, fromName, toName, true); +} + + +static status_t +nfs4_remove_attr(fs_volume* volume, fs_vnode* vnode, const char* name) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Remove(name, NF4NAMEDATTR, NULL); +} + + +static status_t +nfs4_test_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie, + struct flock* lock) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenFileCookie* cookie = reinterpret_cast(_cookie); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, lock = %p", volume, + vti->ID(), _cookie, lock); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->TestLock(cookie, lock); +} + + +static status_t +nfs4_acquire_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie, + const struct flock* lock, bool wait) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenFileCookie* cookie = reinterpret_cast(_cookie); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, lock = %p", volume, + vti->ID(), _cookie, lock); + + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + inode->RevalidateFileCache(); + return inode->AcquireLock(cookie, lock, wait); +} + + +static status_t +nfs4_release_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie, + const struct flock* lock) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" PRIi64 ", cookie = %p, lock = %p", volume, + vti->ID(), _cookie, lock); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) + return B_OK; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + if (lock != NULL) + return inode->ReleaseLock(cookie, lock); + else + return inode->ReleaseAllLocks(cookie); +} + + +status_t +nfs4_init() +{ + gRPCServerManager = new(std::nothrow) RPC::ServerManager; + if (gRPCServerManager == NULL) + return B_NO_MEMORY; + + mutex_init(&gIdMapperLock, "idmapper Init Lock"); + gIdMapper = NULL; + + gWorkQueue = new(std::nothrow) WorkQueue; + if (gWorkQueue == NULL || gWorkQueue->InitStatus() != B_OK) { + delete gWorkQueue; + mutex_destroy(&gIdMapperLock); + delete gRPCServerManager; + return B_NO_MEMORY; + } + + return B_OK; +} + + +status_t +nfs4_uninit() +{ + RPC::CallbackServer::ShutdownAll(); + + delete gIdMapper; + delete gWorkQueue; + delete gRPCServerManager; + + mutex_destroy(&gIdMapperLock); + + return B_OK; +} + + +static status_t +nfs4_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return nfs4_init(); + case B_MODULE_UNINIT: + return nfs4_uninit(); + default: + return B_ERROR; + } +} + + +fs_volume_ops gNFSv4VolumeOps = { + nfs4_unmount, + nfs4_read_fs_info, + NULL, + NULL, + nfs4_get_vnode, +}; + +fs_vnode_ops gNFSv4VnodeOps = { + nfs4_lookup, + nfs4_get_vnode_name, + nfs4_put_vnode, + nfs4_remove_vnode, + + /* VM file access */ + NULL, // can_page() + nfs4_read_pages, + nfs4_write_pages, + + nfs4_io, + NULL, // cancel_io() + + nfs4_get_file_map, + + NULL, // ioctl() + nfs4_set_flags, + NULL, // fs_select() + NULL, // fs_deselect() + nfs4_fsync, + + nfs4_read_symlink, + nfs4_create_symlink, + + nfs4_link, + nfs4_unlink, + nfs4_rename, + + nfs4_access, + nfs4_read_stat, + nfs4_write_stat, + NULL, // fs_preallocate() + + /* file operations */ + nfs4_create, + nfs4_open, + nfs4_close, + nfs4_free_cookie, + nfs4_read, + nfs4_write, + + /* directory operations */ + nfs4_create_dir, + nfs4_remove_dir, + nfs4_open_dir, + nfs4_close_dir, + nfs4_free_dir_cookie, + nfs4_read_dir, + nfs4_rewind_dir, + + /* attribute directory operations */ + nfs4_open_attr_dir, + nfs4_close_attr_dir, + nfs4_free_attr_dir_cookie, + nfs4_read_attr_dir, + nfs4_rewind_attr_dir, + + /* attribute operations */ + nfs4_create_attr, + nfs4_open_attr, + nfs4_close_attr, + nfs4_free_attr_cookie, + nfs4_read_attr, + nfs4_write_attr, + + nfs4_read_attr_stat, + nfs4_write_attr_stat, + nfs4_rename_attr, + nfs4_remove_attr, + + /* support for node and FS layers */ + NULL, // create_special_node + NULL, // get_super_vnode + + /* lock operations */ + nfs4_test_lock, + nfs4_acquire_lock, + nfs4_release_lock, +}; + +static file_system_module_info sNFSv4ModuleInfo = { + { + "file_systems/nfs4" B_CURRENT_FS_API_VERSION, + 0, + nfs4_std_ops, + }, + + "nfs4", // short_name + "Network File System version 4", // pretty_name + + // DDM flags + 0, + + // scanning + NULL, // identify_partition() + NULL, // scan_partition() + NULL, // free_identify_partition_cookie() + NULL, // free_partition_content_cookie() + + nfs4_mount, +}; + +module_info* modules[] = { + (module_info*)&sNFSv4ModuleInfo, + NULL, +}; + diff --git a/src/add-ons/kernel/network/Jamfile b/src/add-ons/kernel/network/Jamfile index 66d4448e41..7667873344 100644 --- a/src/add-ons/kernel/network/Jamfile +++ b/src/add-ons/kernel/network/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/network/dns_resolver/Definitions.h b/src/add-ons/kernel/network/dns_resolver/Definitions.h new file mode 100644 index 0000000000..6aef5c09f8 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/Definitions.h @@ -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 + diff --git a/src/add-ons/kernel/network/dns_resolver/Jamfile b/src/add-ons/kernel/network/dns_resolver/Jamfile new file mode 100644 index 0000000000..8dcbbeb2ea --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/network/dns_resolver/kernel_add_on/Jamfile b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/Jamfile new file mode 100644 index 0000000000..30bc8c5a89 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/Jamfile @@ -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 +; diff --git a/src/add-ons/kernel/network/dns_resolver/kernel_add_on/dns_resolver.cpp b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/dns_resolver.cpp new file mode 100644 index 0000000000..476b3948fd --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/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 + +#include +#include +#include +#include +#include +#include + +#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(-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(addr); + + struct addrinfo* current = addr; + while (current != NULL) { + uint64 addrOffset = reinterpret_cast(current->ai_addr); + uint64 nameOffset = reinterpret_cast(current->ai_canonname); + uint64 nextOffset = reinterpret_cast(current->ai_next); + + if (current->ai_addr != NULL) { + current->ai_addr + = reinterpret_cast(generalOffset + addrOffset); + } + + if (current->ai_canonname != NULL) + current->ai_canonname = generalOffset + nameOffset; + + if (current->ai_next != NULL) { + current->ai_next + = reinterpret_cast(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(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(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(reply); + RelocateEntries(addr); + *res = addr; + return B_OK; + case MsgError: + result = *reinterpret_cast(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 +}; + diff --git a/src/add-ons/kernel/network/dns_resolver/server/Jamfile b/src/add-ons/kernel/network/dns_resolver/server/Jamfile new file mode 100644 index 0000000000..e2fbbaad61 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/server/Jamfile @@ -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 +; diff --git a/src/add-ons/kernel/network/dns_resolver/server/dns_resolver_server.rdef b/src/add-ons/kernel/network/dns_resolver/server/dns_resolver_server.rdef new file mode 100644 index 0000000000..e0a0df5a41 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/server/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" +}; + diff --git a/src/add-ons/kernel/network/dns_resolver/server/main.cpp b/src/add-ons/kernel/network/dns_resolver/server/main.cpp new file mode 100644 index 0000000000..f82d405427 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/server/main.cpp @@ -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 +#include + +#include +#include + +#include +#include +#include + +#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(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(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(sockPos); + sockPos += sizeof(sockaddr_in); + } else { + memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in6)); + temp.ai_addr = reinterpret_cast(sockPos); + sockPos += sizeof(sockaddr_in6); + } + } + + addrinfo* next = current->ai_next; + if (next != NULL) + temp.ai_next = reinterpret_cast(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(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(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(); +} + diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index ebca444cb1..27613f88f0 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -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; diff --git a/src/system/kernel/util/Jamfile b/src/system/kernel/util/Jamfile index 59ff30c7c6..02ce7ce690 100644 --- a/src/system/kernel/util/Jamfile +++ b/src/system/kernel/util/Jamfile @@ -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 diff --git a/src/system/kernel/util/hostname.cpp b/src/system/kernel/util/hostname.cpp new file mode 100644 index 0000000000..676f16990e --- /dev/null +++ b/src/system/kernel/util/hostname.cpp @@ -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 +#include +#include +#include + +#include +#include + +#include + + +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; +} + diff --git a/src/system/kernel/util/inet_addr.c b/src/system/kernel/util/inet_addr.c new file mode 100644 index 0000000000..dca5d40cd1 --- /dev/null +++ b/src/system/kernel/util/inet_addr.c @@ -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 +#include + +#include +#include + +#include + +/*% + * 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); +} + diff --git a/src/system/kernel/util/inet_ntop.c b/src/system/kernel/util/inet_ntop.c new file mode 100644 index 0000000000..ba3605e760 --- /dev/null +++ b/src/system/kernel/util/inet_ntop.c @@ -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 +#include +#include + +#include +#include +#include + +#include +#include +#include + +#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); +} +