Merge branch 'nfs4'
Conflicts: build/jam/HaikuImage
This commit is contained in:
@@ -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 ;
|
||||
|
||||
@@ -18,7 +18,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures
|
||||
hd head hey hostname
|
||||
id ident ifconfig <bin>install installsound iroster isvolume
|
||||
ideinfo@ide idestatus@ide
|
||||
join kernel_debugger keymap keystore kill
|
||||
join kernel_debugger keymap kill
|
||||
less lessecho lesskey link linkcatkeys listarea listattr listimage listdev
|
||||
listport listres listsem listusb ln locale locate logger login logname ls
|
||||
lsindex
|
||||
@@ -84,10 +84,10 @@ PRIVATE_SYSTEM_LIBS = [ FFilterByBuildFeatures
|
||||
libilmimf.so
|
||||
] ;
|
||||
SYSTEM_SERVERS = [ FFilterByBuildFeatures
|
||||
app_server cddb_daemon debug_server input_server keystore_server mail_daemon
|
||||
app_server cddb_daemon debug_server input_server mail_daemon
|
||||
media_addon_server media_server midi_server mount_server net_server
|
||||
notification_server power_daemon print_server print_addon_server registrar
|
||||
syslog_daemon
|
||||
syslog_daemon dns_resolver_server nfs4_idmapper_server
|
||||
] ;
|
||||
|
||||
SYSTEM_NETWORK_DEVICES = ethernet loopback ;
|
||||
@@ -183,7 +183,7 @@ SYSTEM_ADD_ONS_BUS_MANAGERS = [ FFilterByBuildFeatures
|
||||
ata@ata pci ps2@x86 isa@x86
|
||||
ide@ide scsi config_manager agp_gart usb firewire acpi@x86
|
||||
] ;
|
||||
SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs btrfs cdda exfat ext2 fat iso9660 nfs
|
||||
SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs btrfs cdda exfat ext2 fat iso9660 nfs nfs4
|
||||
attribute_overlay write_overlay ntfs reiserfs udf googlefs ;
|
||||
|
||||
# wifi firmware
|
||||
@@ -232,7 +232,7 @@ AddFilesToHaikuImage system add-ons kernel file_systems
|
||||
: $(SYSTEM_ADD_ONS_FILE_SYSTEMS) ;
|
||||
AddFilesToHaikuImage system add-ons kernel generic
|
||||
: ata_adapter@ata bios@x86 dpc ide_adapter@ide
|
||||
locked_pool mpu401 scsi_periph <module>tty cpuidle@x86 ;
|
||||
locked_pool mpu401 scsi_periph <module>tty ; #cpuidle@x86 ;
|
||||
AddFilesToHaikuImage system add-ons kernel partitioning_systems
|
||||
: amiga_rdb apple efi_gpt intel session ;
|
||||
AddFilesToHaikuImage system add-ons kernel interrupt_controllers
|
||||
@@ -246,7 +246,7 @@ if $(TARGET_ARCH) = x86 {
|
||||
AddNewDriversToHaikuImage disk scsi : scsi_cd scsi_disk ;
|
||||
AddNewDriversToHaikuImage power : enhanced_speedstep@x86 ;
|
||||
AddNewDriversToHaikuImage power : acpi_battery@x86 ;
|
||||
AddNewDriversToHaikuImage power : x86_cpuidle@x86 ;
|
||||
#AddNewDriversToHaikuImage power : x86_cpuidle@x86 ;
|
||||
#AddNewDriversToHaikuImage display : display_controls@x86 ;
|
||||
|
||||
# legacy drivers
|
||||
@@ -663,6 +663,7 @@ AddFilesToHaikuImage system add-ons input_server filters
|
||||
: screen_saver shortcut_catcher ;
|
||||
AddFilesToHaikuImage system add-ons kernel network
|
||||
: <net>notifications stack ;
|
||||
AddFilesToHaikuImage system add-ons kernel network : dns_resolver ;
|
||||
AddFilesToHaikuImage system add-ons kernel network devices
|
||||
: $(SYSTEM_NETWORK_DEVICES) ;
|
||||
AddFilesToHaikuImage system add-ons kernel network datalink_protocols
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -269,6 +269,7 @@ _AVL_TREE_MAP_CLASS_NAME::MakeEmpty()
|
||||
{
|
||||
AVLTreeNode* root = fTree.Root();
|
||||
_FreeTree(root);
|
||||
fTree.MakeEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef DNS_RESOLVER_H
|
||||
#define DNS_RESOLVER_H
|
||||
|
||||
|
||||
#include <netdb.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <module.h>
|
||||
|
||||
|
||||
#define DNS_RESOLVER_MODULE_NAME "network/dns_resolver/v1"
|
||||
|
||||
|
||||
struct dns_resolver_module {
|
||||
module_info module;
|
||||
status_t (*getaddrinfo)(const char* node, const char* service,
|
||||
const struct addrinfo* hints, struct addrinfo** res);
|
||||
};
|
||||
|
||||
|
||||
static inline int
|
||||
kgetaddrinfo(const char* node, const char* service,
|
||||
const struct addrinfo* hints, struct addrinfo** res)
|
||||
{
|
||||
dns_resolver_module* dns;
|
||||
status_t result = get_module(DNS_RESOLVER_MODULE_NAME,
|
||||
reinterpret_cast<module_info**>(&dns));
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
result = dns->getaddrinfo(node, service, hints, res);
|
||||
|
||||
put_module(DNS_RESOLVER_MODULE_NAME);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
kfreeaddrinfo(struct addrinfo* res)
|
||||
{
|
||||
free(res);
|
||||
}
|
||||
|
||||
|
||||
#define getaddrinfo kgetaddrinfo
|
||||
#define freeaddrinfo kfreeaddrinfo
|
||||
|
||||
|
||||
#endif // DNS_RESOLVER_H
|
||||
|
||||
@@ -11,6 +11,7 @@ SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems iso9660 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems netfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems ntfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems packagefs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems ramfs ;
|
||||
|
||||
@@ -0,0 +1,798 @@
|
||||
/*
|
||||
* Copyright 2012-2013 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
#include <net/dns_resolver.h>
|
||||
|
||||
|
||||
#define NFS4_PORT 2049
|
||||
|
||||
#define LAST_FRAGMENT 0x80000000
|
||||
#define MAX_PACKET_SIZE 65535
|
||||
|
||||
#define NFS_MIN_PORT 665
|
||||
|
||||
|
||||
bool
|
||||
PeerAddress::operator==(const PeerAddress& address)
|
||||
{
|
||||
return memcmp(&fAddress, &address.fAddress, sizeof(fAddress)) == 0
|
||||
&& fProtocol == address.fProtocol;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
PeerAddress::operator<(const PeerAddress& address)
|
||||
{
|
||||
int compare = memcmp(&fAddress, &address.fAddress, sizeof(fAddress));
|
||||
return compare < 0 || (compare == 0 && fProtocol < address.fProtocol);
|
||||
}
|
||||
|
||||
|
||||
PeerAddress&
|
||||
PeerAddress::operator=(const PeerAddress& address)
|
||||
{
|
||||
fAddress = address.fAddress;
|
||||
fProtocol = address.fProtocol;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PeerAddress::PeerAddress()
|
||||
:
|
||||
fProtocol(0)
|
||||
{
|
||||
memset(&fAddress, 0, sizeof(fAddress));
|
||||
}
|
||||
|
||||
|
||||
PeerAddress::PeerAddress(int networkFamily)
|
||||
:
|
||||
fProtocol(0)
|
||||
{
|
||||
ASSERT(networkFamily == AF_INET || networkFamily == AF_INET6);
|
||||
|
||||
memset(&fAddress, 0, sizeof(fAddress));
|
||||
|
||||
fAddress.ss_family = networkFamily;
|
||||
switch (networkFamily) {
|
||||
case AF_INET:
|
||||
fAddress.ss_len = sizeof(sockaddr_in);
|
||||
break;
|
||||
case AF_INET6:
|
||||
fAddress.ss_len = sizeof(sockaddr_in6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
PeerAddress::ProtocolString() const
|
||||
{
|
||||
static const char* tcpName = "tcp";
|
||||
static const char* udpName = "udp";
|
||||
static const char* unknown = "";
|
||||
|
||||
switch (fProtocol) {
|
||||
case IPPROTO_TCP:
|
||||
return tcpName;
|
||||
case IPPROTO_UDP:
|
||||
return udpName;
|
||||
default:
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PeerAddress::SetProtocol(const char* protocol)
|
||||
{
|
||||
ASSERT(protocol != NULL);
|
||||
|
||||
if (strcmp(protocol, "tcp") == 0)
|
||||
fProtocol = IPPROTO_TCP;
|
||||
else if (strcmp(protocol, "udp") == 0)
|
||||
fProtocol = IPPROTO_UDP;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
PeerAddress::UniversalAddress() const
|
||||
{
|
||||
char* uAddr = reinterpret_cast<char*>(malloc(INET6_ADDRSTRLEN + 16));
|
||||
if (uAddr == NULL)
|
||||
return NULL;
|
||||
|
||||
if (inet_ntop(fAddress.ss_family, InAddr(), uAddr, AddressSize()) == NULL)
|
||||
return NULL;
|
||||
|
||||
char port[16];
|
||||
sprintf(port, ".%d.%d", Port() >> 8, Port() & 0xff);
|
||||
strcat(uAddr, port);
|
||||
|
||||
return uAddr;
|
||||
}
|
||||
|
||||
|
||||
socklen_t
|
||||
PeerAddress::AddressSize() const
|
||||
{
|
||||
switch (Family()) {
|
||||
case AF_INET:
|
||||
return sizeof(sockaddr_in);
|
||||
case AF_INET6:
|
||||
return sizeof(sockaddr_in6);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint16
|
||||
PeerAddress::Port() const
|
||||
{
|
||||
uint16 port;
|
||||
|
||||
switch (Family()) {
|
||||
case AF_INET:
|
||||
port = reinterpret_cast<const sockaddr_in*>(&fAddress)->sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
port = reinterpret_cast<const sockaddr_in6*>(&fAddress)->sin6_port;
|
||||
break;
|
||||
default:
|
||||
port = 0;
|
||||
}
|
||||
|
||||
return ntohs(port);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PeerAddress::SetPort(uint16 port)
|
||||
{
|
||||
port = htons(port);
|
||||
|
||||
switch (Family()) {
|
||||
case AF_INET:
|
||||
reinterpret_cast<sockaddr_in*>(&fAddress)->sin_port = port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
reinterpret_cast<sockaddr_in6*>(&fAddress)->sin6_port = port;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const void*
|
||||
PeerAddress::InAddr() const
|
||||
{
|
||||
switch (Family()) {
|
||||
case AF_INET:
|
||||
return &reinterpret_cast<const sockaddr_in*>(&fAddress)->sin_addr;
|
||||
case AF_INET6:
|
||||
return &reinterpret_cast<const sockaddr_in6*>(&fAddress)->sin6_addr;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
PeerAddress::InAddrSize() const
|
||||
{
|
||||
switch (Family()) {
|
||||
case AF_INET:
|
||||
return sizeof(in_addr);
|
||||
case AF_INET6:
|
||||
return sizeof(in6_addr);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AddressResolver::AddressResolver(const char* name)
|
||||
:
|
||||
fHead(NULL),
|
||||
fCurrent(NULL),
|
||||
fForcedPort(htons(NFS4_PORT)),
|
||||
fForcedProtocol(IPPROTO_TCP)
|
||||
{
|
||||
fStatus = ResolveAddress(name);
|
||||
}
|
||||
|
||||
|
||||
AddressResolver::~AddressResolver()
|
||||
{
|
||||
freeaddrinfo(fHead);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AddressResolver::ResolveAddress(const char* name)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
|
||||
if (fHead != NULL) {
|
||||
freeaddrinfo(fHead);
|
||||
fHead = NULL;
|
||||
fCurrent = NULL;
|
||||
}
|
||||
|
||||
// getaddrinfo() is very expensive when called from kernel, so we do not
|
||||
// want to call it unless there is no other choice.
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
if (inet_aton(name, &addr.sin_addr) == 1) {
|
||||
addr.sin_len = sizeof(addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(NFS4_PORT);
|
||||
|
||||
memcpy(&fAddress.fAddress, &addr, sizeof(addr));
|
||||
fAddress.fProtocol = IPPROTO_TCP;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t result = getaddrinfo(name, NULL, NULL, &fHead);
|
||||
fCurrent = fHead;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AddressResolver::ForceProtocol(const char* protocol)
|
||||
{
|
||||
ASSERT(protocol != NULL);
|
||||
|
||||
if (strcmp(protocol, "tcp") == 0)
|
||||
fForcedProtocol = IPPROTO_TCP;
|
||||
else if (strcmp(protocol, "udp") == 0)
|
||||
fForcedProtocol = IPPROTO_UDP;
|
||||
|
||||
fAddress.SetProtocol(protocol);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AddressResolver::ForcePort(uint16 port)
|
||||
{
|
||||
fForcedPort = htons(port);
|
||||
fAddress.SetPort(port);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AddressResolver::GetNextAddress(PeerAddress* address)
|
||||
{
|
||||
ASSERT(address != NULL);
|
||||
|
||||
if (fStatus != B_OK)
|
||||
return fStatus;
|
||||
|
||||
if (fHead == NULL) {
|
||||
*address = fAddress;
|
||||
fStatus = B_NAME_NOT_FOUND;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
address->fProtocol = fForcedProtocol;
|
||||
|
||||
while (fCurrent != NULL) {
|
||||
if (fCurrent->ai_family == AF_INET) {
|
||||
memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in));
|
||||
reinterpret_cast<sockaddr_in*>(&address->fAddress)->sin_port
|
||||
= fForcedPort;
|
||||
} else if (fCurrent->ai_family == AF_INET6) {
|
||||
memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in6));
|
||||
reinterpret_cast<sockaddr_in6*>(&address->fAddress)->sin6_port
|
||||
= fForcedPort;
|
||||
} else {
|
||||
fCurrent = fCurrent->ai_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
fCurrent = fCurrent->ai_next;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
Connection::Connection(const PeerAddress& address)
|
||||
:
|
||||
ConnectionBase(address)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ConnectionListener::ConnectionListener(const PeerAddress& address)
|
||||
:
|
||||
ConnectionBase(address)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ConnectionBase::ConnectionBase(const PeerAddress& address)
|
||||
:
|
||||
fWaitCancel(create_sem(0, NULL)),
|
||||
fSocket(-1),
|
||||
fPeerAddress(address)
|
||||
{
|
||||
mutex_init(&fSocketLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
ConnectionStream::ConnectionStream(const PeerAddress& address)
|
||||
:
|
||||
Connection(address)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ConnectionPacket::ConnectionPacket(const PeerAddress& address)
|
||||
:
|
||||
Connection(address)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ConnectionBase::~ConnectionBase()
|
||||
{
|
||||
if (fSocket != -1)
|
||||
close(fSocket);
|
||||
mutex_destroy(&fSocketLock);
|
||||
delete_sem(fWaitCancel);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ConnectionBase::GetLocalAddress(PeerAddress* address)
|
||||
{
|
||||
ASSERT(address != NULL);
|
||||
|
||||
address->fProtocol = fPeerAddress.fProtocol;
|
||||
|
||||
socklen_t addressSize = sizeof(address->fAddress);
|
||||
return getsockname(fSocket, (struct sockaddr*)&address->fAddress,
|
||||
&addressSize);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ConnectionStream::Send(const void* buffer, uint32 size)
|
||||
{
|
||||
ASSERT(buffer != NULL);
|
||||
|
||||
status_t result;
|
||||
|
||||
uint32* buf = reinterpret_cast<uint32*>(malloc(size + sizeof(uint32)));
|
||||
if (buf == NULL)
|
||||
return B_NO_MEMORY;
|
||||
MemoryDeleter _(buf);
|
||||
|
||||
buf[0] = htonl(size | LAST_FRAGMENT);
|
||||
memcpy(buf + 1, buffer, size);
|
||||
|
||||
// More than one threads may send data and ksend is allowed to send partial
|
||||
// data. Need a lock here.
|
||||
uint32 sent = 0;
|
||||
mutex_lock(&fSocketLock);
|
||||
do {
|
||||
result = send(fSocket, buf + sent, size + sizeof(uint32) - sent, 0);
|
||||
sent += result;
|
||||
} while (result > 0 && sent < size + sizeof(uint32));
|
||||
mutex_unlock(&fSocketLock);
|
||||
if (result < 0) {
|
||||
result = errno;
|
||||
return result;
|
||||
} else if (result == 0)
|
||||
return B_IO_ERROR;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ConnectionPacket::Send(const void* buffer, uint32 size)
|
||||
{
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(size < 65535);
|
||||
|
||||
// send on DGRAM sockets is atomic. No need to lock.
|
||||
status_t result = send(fSocket, buffer, size, 0);
|
||||
if (result < 0)
|
||||
return errno;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ConnectionStream::Receive(void** _buffer, uint32* _size)
|
||||
{
|
||||
ASSERT(_buffer != NULL);
|
||||
ASSERT(_size != NULL);
|
||||
|
||||
status_t result;
|
||||
|
||||
uint32 size = 0;
|
||||
void* buffer = NULL;
|
||||
|
||||
uint32 record_size;
|
||||
bool last_one = false;
|
||||
|
||||
object_wait_info object[2];
|
||||
object[0].object = fWaitCancel;
|
||||
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
|
||||
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
|
||||
object[1].object = fSocket;
|
||||
object[1].type = B_OBJECT_TYPE_FD;
|
||||
object[1].events = B_EVENT_READ;
|
||||
|
||||
do {
|
||||
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
object[1].events = B_EVENT_READ;
|
||||
|
||||
result = wait_for_objects(object, 2);
|
||||
if (result < B_OK
|
||||
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
|
||||
free(buffer);
|
||||
return ECONNABORTED;
|
||||
} else if ((object[1].events & B_EVENT_READ) == 0)
|
||||
continue;
|
||||
|
||||
// There is only one listener thread per connection. No need to lock.
|
||||
uint32 received = 0;
|
||||
do {
|
||||
result = recv(fSocket, ((uint8*)&record_size) + received,
|
||||
sizeof(record_size) - received, 0);
|
||||
received += result;
|
||||
} while (result > 0 && received < sizeof(record_size));
|
||||
if (result < 0) {
|
||||
result = errno;
|
||||
free(buffer);
|
||||
return result;
|
||||
} else if (result == 0) {
|
||||
free(buffer);
|
||||
return ECONNABORTED;
|
||||
}
|
||||
|
||||
record_size = ntohl(record_size);
|
||||
ASSERT(record_size > 0);
|
||||
|
||||
last_one = static_cast<int32>(record_size) < 0;
|
||||
record_size &= LAST_FRAGMENT - 1;
|
||||
|
||||
void* ptr = realloc(buffer, size + record_size);
|
||||
if (ptr == NULL) {
|
||||
free(buffer);
|
||||
return B_NO_MEMORY;
|
||||
} else
|
||||
buffer = ptr;
|
||||
MemoryDeleter bufferDeleter(buffer);
|
||||
|
||||
received = 0;
|
||||
do {
|
||||
result = recv(fSocket, (uint8*)buffer + size + received,
|
||||
record_size - received, 0);
|
||||
received += result;
|
||||
} while (result > 0 && received < record_size);
|
||||
if (result < 0)
|
||||
return errno;
|
||||
else if (result == 0)
|
||||
return ECONNABORTED;
|
||||
|
||||
bufferDeleter.Detach();
|
||||
size += record_size;
|
||||
} while (!last_one);
|
||||
|
||||
|
||||
*_buffer = buffer;
|
||||
*_size = size;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ConnectionPacket::Receive(void** _buffer, uint32* _size)
|
||||
{
|
||||
ASSERT(_buffer != NULL);
|
||||
ASSERT(_size != NULL);
|
||||
|
||||
status_t result;
|
||||
int32 size = MAX_PACKET_SIZE;
|
||||
void* buffer = malloc(size);
|
||||
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
object_wait_info object[2];
|
||||
object[0].object = fWaitCancel;
|
||||
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
|
||||
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
|
||||
object[1].object = fSocket;
|
||||
object[1].type = B_OBJECT_TYPE_FD;
|
||||
object[1].events = B_EVENT_READ;
|
||||
|
||||
do {
|
||||
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
object[1].events = B_EVENT_READ;
|
||||
|
||||
result = wait_for_objects(object, 2);
|
||||
if (result < B_OK
|
||||
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
|
||||
free(buffer);
|
||||
return ECONNABORTED;
|
||||
} else if ((object[1].events & B_EVENT_READ) == 0)
|
||||
continue;
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
// There is only one listener thread per connection. No need to lock.
|
||||
size = recv(fSocket, buffer, size, 0);
|
||||
if (size < 0) {
|
||||
result = errno;
|
||||
free(buffer);
|
||||
return result;
|
||||
} else if (size == 0) {
|
||||
free(buffer);
|
||||
return ECONNABORTED;
|
||||
}
|
||||
|
||||
*_buffer = buffer;
|
||||
*_size = size;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
Connection*
|
||||
Connection::CreateObject(const PeerAddress& address)
|
||||
{
|
||||
switch (address.fProtocol) {
|
||||
case IPPROTO_TCP:
|
||||
return new(std::nothrow) ConnectionStream(address);
|
||||
case IPPROTO_UDP:
|
||||
return new(std::nothrow) ConnectionPacket(address);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Connection::Connect(Connection **_connection, const PeerAddress& address)
|
||||
{
|
||||
ASSERT(_connection != NULL);
|
||||
|
||||
Connection* conn = CreateObject(address);
|
||||
if (conn == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t result;
|
||||
if (conn->fWaitCancel < B_OK) {
|
||||
result = conn->fWaitCancel;
|
||||
delete conn;
|
||||
return result;
|
||||
}
|
||||
|
||||
result = conn->Connect();
|
||||
if (result != B_OK) {
|
||||
delete conn;
|
||||
return result;
|
||||
}
|
||||
|
||||
*_connection = conn;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Connection::SetTo(Connection **_connection, int socket,
|
||||
const PeerAddress& address)
|
||||
{
|
||||
ASSERT(_connection != NULL);
|
||||
ASSERT(socket != -1);
|
||||
|
||||
Connection* conn = CreateObject(address);
|
||||
if (conn == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t result;
|
||||
if (conn->fWaitCancel < B_OK) {
|
||||
result = conn->fWaitCancel;
|
||||
delete conn;
|
||||
return result;
|
||||
}
|
||||
|
||||
conn->fSocket = socket;
|
||||
|
||||
*_connection = conn;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Connection::Connect()
|
||||
{
|
||||
switch (fPeerAddress.fProtocol) {
|
||||
case IPPROTO_TCP:
|
||||
fSocket = socket(fPeerAddress.Family(), SOCK_STREAM, IPPROTO_TCP);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
fSocket = socket(fPeerAddress.Family(), SOCK_DGRAM, IPPROTO_UDP);
|
||||
break;
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
if (fSocket < 0)
|
||||
return errno;
|
||||
|
||||
status_t result;
|
||||
uint16 port, attempt = 0;
|
||||
|
||||
PeerAddress address(fPeerAddress.Family());
|
||||
|
||||
do {
|
||||
port = rand() % (IPPORT_RESERVED - NFS_MIN_PORT);
|
||||
port += NFS_MIN_PORT;
|
||||
|
||||
if (attempt == 9)
|
||||
port = 0;
|
||||
attempt++;
|
||||
|
||||
address.SetPort(port);
|
||||
result = bind(fSocket, (sockaddr*)&address.fAddress,
|
||||
address.AddressSize());
|
||||
} while (attempt <= 10 && result != B_OK);
|
||||
|
||||
if (attempt > 10) {
|
||||
close(fSocket);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = connect(fSocket, (sockaddr*)&fPeerAddress.fAddress,
|
||||
fPeerAddress.AddressSize());
|
||||
if (result != 0) {
|
||||
result = errno;
|
||||
close(fSocket);
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Connection::Reconnect()
|
||||
{
|
||||
release_sem(fWaitCancel);
|
||||
close(fSocket);
|
||||
acquire_sem(fWaitCancel);
|
||||
return Connect();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ConnectionBase::Disconnect()
|
||||
{
|
||||
release_sem(fWaitCancel);
|
||||
|
||||
close(fSocket);
|
||||
fSocket = -1;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ConnectionListener::Listen(ConnectionListener** listener, int networkFamily,
|
||||
uint16 port)
|
||||
{
|
||||
ASSERT(listener != NULL);
|
||||
ASSERT(networkFamily == AF_INET || networkFamily == AF_INET6);
|
||||
|
||||
int sock = socket(networkFamily, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock < 0)
|
||||
return errno;
|
||||
|
||||
PeerAddress address(networkFamily);
|
||||
address.SetPort(port);
|
||||
address.fProtocol = IPPROTO_TCP;
|
||||
|
||||
status_t result = bind(sock, (sockaddr*)&address.fAddress,
|
||||
address.AddressSize());
|
||||
if (result != B_OK) {
|
||||
close(sock);
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (listen(sock, 5) != B_OK) {
|
||||
close(sock);
|
||||
return errno;
|
||||
}
|
||||
|
||||
*listener = new(std::nothrow) ConnectionListener(address);
|
||||
if (*listener == NULL) {
|
||||
close(sock);
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((*listener)->fWaitCancel < B_OK) {
|
||||
result = (*listener)->fWaitCancel;
|
||||
close(sock);
|
||||
delete *listener;
|
||||
return result;
|
||||
}
|
||||
|
||||
(*listener)->fSocket = sock;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ConnectionListener::AcceptConnection(Connection** connection)
|
||||
{
|
||||
ASSERT(connection != NULL);
|
||||
|
||||
object_wait_info object[2];
|
||||
object[0].object = fWaitCancel;
|
||||
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
|
||||
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
|
||||
object[1].object = fSocket;
|
||||
object[1].type = B_OBJECT_TYPE_FD;
|
||||
object[1].events = B_EVENT_READ;
|
||||
|
||||
do {
|
||||
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
object[1].events = B_EVENT_READ;
|
||||
|
||||
status_t result = wait_for_objects(object, 2);
|
||||
if (result < B_OK
|
||||
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
|
||||
return ECONNABORTED;
|
||||
} else if ((object[1].events & B_EVENT_READ) == 0)
|
||||
continue;
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
sockaddr_storage addr;
|
||||
socklen_t length = sizeof(addr);
|
||||
int sock = accept(fSocket, reinterpret_cast<sockaddr*>(&addr), &length);
|
||||
if (sock < 0)
|
||||
return errno;
|
||||
|
||||
PeerAddress address;
|
||||
address.fProtocol = IPPROTO_TCP;
|
||||
address.fAddress = addr;
|
||||
|
||||
status_t result = Connection::SetTo(connection, sock, address);
|
||||
if (result != B_OK) {
|
||||
close(sock);
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2012-2013 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
struct PeerAddress {
|
||||
sockaddr_storage fAddress;
|
||||
int fProtocol;
|
||||
|
||||
bool operator==(const PeerAddress& address);
|
||||
bool operator<(const PeerAddress& address);
|
||||
|
||||
PeerAddress& operator=(const PeerAddress& address);
|
||||
|
||||
PeerAddress();
|
||||
PeerAddress(int networkFamily);
|
||||
|
||||
inline int Family() const;
|
||||
|
||||
const char* ProtocolString() const;
|
||||
void SetProtocol(const char* protocol);
|
||||
|
||||
char* UniversalAddress() const;
|
||||
|
||||
socklen_t AddressSize() const;
|
||||
|
||||
void SetPort(uint16 port);
|
||||
uint16 Port() const;
|
||||
|
||||
const void* InAddr() const;
|
||||
size_t InAddrSize() const;
|
||||
};
|
||||
|
||||
|
||||
inline int
|
||||
PeerAddress::Family() const
|
||||
{
|
||||
return fAddress.ss_family;
|
||||
}
|
||||
|
||||
|
||||
struct addrinfo;
|
||||
|
||||
class AddressResolver {
|
||||
public:
|
||||
AddressResolver(const char* name);
|
||||
~AddressResolver();
|
||||
|
||||
status_t GetNextAddress(PeerAddress* address);
|
||||
|
||||
void ForceProtocol(const char* protocol);
|
||||
void ForcePort(uint16 port);
|
||||
|
||||
protected:
|
||||
status_t ResolveAddress(const char* name);
|
||||
|
||||
private:
|
||||
addrinfo* fHead;
|
||||
addrinfo* fCurrent;
|
||||
|
||||
PeerAddress fAddress;
|
||||
|
||||
uint16 fForcedPort;
|
||||
int fForcedProtocol;
|
||||
|
||||
status_t fStatus;
|
||||
};
|
||||
|
||||
class ConnectionBase {
|
||||
public:
|
||||
ConnectionBase(const PeerAddress& address);
|
||||
virtual ~ConnectionBase();
|
||||
|
||||
status_t GetLocalAddress(PeerAddress* address);
|
||||
|
||||
void Disconnect();
|
||||
|
||||
protected:
|
||||
sem_id fWaitCancel;
|
||||
int fSocket;
|
||||
mutex fSocketLock;
|
||||
|
||||
const PeerAddress fPeerAddress;
|
||||
};
|
||||
|
||||
class Connection : public ConnectionBase {
|
||||
public:
|
||||
static status_t Connect(Connection **connection,
|
||||
const PeerAddress& address);
|
||||
static status_t SetTo(Connection **connection, int socket,
|
||||
const PeerAddress& address);
|
||||
|
||||
virtual status_t Send(const void* buffer, uint32 size) = 0;
|
||||
virtual status_t Receive(void** buffer, uint32* size) = 0;
|
||||
|
||||
status_t Reconnect();
|
||||
|
||||
protected:
|
||||
static Connection* CreateObject(const PeerAddress& address);
|
||||
|
||||
Connection(const PeerAddress& address);
|
||||
status_t Connect();
|
||||
|
||||
};
|
||||
|
||||
class ConnectionStream : public Connection {
|
||||
public:
|
||||
ConnectionStream(const PeerAddress& address);
|
||||
|
||||
virtual status_t Send(const void* buffer, uint32 size);
|
||||
virtual status_t Receive(void** buffer, uint32* size);
|
||||
};
|
||||
|
||||
class ConnectionPacket : public Connection {
|
||||
public:
|
||||
ConnectionPacket(const PeerAddress& address);
|
||||
|
||||
virtual status_t Send(const void* buffer, uint32 size);
|
||||
virtual status_t Receive(void** buffer, uint32* size);
|
||||
};
|
||||
|
||||
class ConnectionListener : public ConnectionBase {
|
||||
public:
|
||||
static status_t Listen(ConnectionListener** listener, int networkFamily,
|
||||
uint16 port = 0);
|
||||
|
||||
status_t AcceptConnection(Connection** connection);
|
||||
|
||||
protected:
|
||||
ConnectionListener(const PeerAddress& address);
|
||||
};
|
||||
|
||||
#endif // CONNECTION_H
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "Cookie.h"
|
||||
|
||||
#include "Inode.h"
|
||||
#include "Request.h"
|
||||
|
||||
|
||||
LockOwner::LockOwner(uint32 owner)
|
||||
:
|
||||
fSequence(0),
|
||||
fOwner(owner),
|
||||
fUseCount(0),
|
||||
fNext(NULL),
|
||||
fPrev(NULL)
|
||||
{
|
||||
memset(fStateId, 0, sizeof(fStateId));
|
||||
mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
LockOwner::~LockOwner()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
LockInfo::LockInfo(LockOwner* owner)
|
||||
:
|
||||
fOwner(owner)
|
||||
{
|
||||
ASSERT(owner != NULL);
|
||||
fOwner->fUseCount++;
|
||||
}
|
||||
|
||||
|
||||
LockInfo::~LockInfo()
|
||||
{
|
||||
fOwner->fUseCount--;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LockInfo::operator==(const struct flock& lock) const
|
||||
{
|
||||
bool eof = lock.l_len + lock.l_start == OFF_MAX;
|
||||
uint64 start = static_cast<uint64>(lock.l_start);
|
||||
uint64 len = static_cast<uint64>(lock.l_len);
|
||||
|
||||
return fStart == start && (fLength == len
|
||||
|| (eof && fLength == UINT64_MAX));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LockInfo::operator==(const LockInfo& lock) const
|
||||
{
|
||||
return fOwner == lock.fOwner && fStart == lock.fStart
|
||||
&& fLength == lock.fLength && fType == lock.fType;
|
||||
}
|
||||
|
||||
|
||||
Cookie::Cookie()
|
||||
:
|
||||
fRequests(NULL),
|
||||
fSnoozeCancel(create_sem(1, NULL))
|
||||
{
|
||||
acquire_sem(fSnoozeCancel);
|
||||
mutex_init(&fRequestLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
Cookie::~Cookie()
|
||||
{
|
||||
delete_sem(fSnoozeCancel);
|
||||
mutex_destroy(&fRequestLock);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Cookie::RegisterRequest(RPC::Request* req)
|
||||
{
|
||||
ASSERT(req != NULL);
|
||||
|
||||
RequestEntry* ent = new RequestEntry;
|
||||
if (ent == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
MutexLocker _(fRequestLock);
|
||||
ent->fRequest = req;
|
||||
ent->fNext = fRequests;
|
||||
fRequests = ent;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Cookie::UnregisterRequest(RPC::Request* req)
|
||||
{
|
||||
ASSERT(req != NULL);
|
||||
|
||||
MutexLocker _(fRequestLock);
|
||||
RequestEntry* ent = fRequests;
|
||||
RequestEntry* prev = NULL;
|
||||
while (ent != NULL) {
|
||||
if (ent->fRequest == req) {
|
||||
if (prev == NULL)
|
||||
fRequests = ent->fNext;
|
||||
else
|
||||
prev->fNext = ent->fNext;
|
||||
delete ent;
|
||||
}
|
||||
|
||||
prev = ent;
|
||||
ent = ent->fNext;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Cookie::CancelAll()
|
||||
{
|
||||
release_sem(fSnoozeCancel);
|
||||
|
||||
MutexLocker _(fRequestLock);
|
||||
RequestEntry* ent = fRequests;
|
||||
while (ent != NULL) {
|
||||
fFileSystem->Server()->WakeCall(ent->fRequest);
|
||||
ent = ent->fNext;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
OpenFileCookie::OpenFileCookie()
|
||||
:
|
||||
fLocks(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OpenFileCookie::AddLock(LockInfo* lock)
|
||||
{
|
||||
ASSERT(lock != NULL);
|
||||
|
||||
lock->fCookieNext = fLocks;
|
||||
fLocks = lock;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OpenFileCookie::RemoveLock(LockInfo* lock, LockInfo* prev)
|
||||
{
|
||||
if (prev != NULL)
|
||||
prev->fCookieNext = lock->fCookieNext;
|
||||
else {
|
||||
ASSERT(prev == NULL && fLocks == lock);
|
||||
fLocks = lock->fCookieNext;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OpenDirCookie::~OpenDirCookie()
|
||||
{
|
||||
if (fSnapshot != NULL)
|
||||
fSnapshot->ReleaseReference();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef COOKIE_H
|
||||
#define COOKIE_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "DirectoryCache.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
|
||||
struct OpenState;
|
||||
|
||||
struct LockOwner {
|
||||
uint64 fClientId;
|
||||
|
||||
uint32 fStateId[3];
|
||||
uint32 fStateSeq;
|
||||
uint32 fSequence;
|
||||
|
||||
uint32 fOwner;
|
||||
|
||||
uint32 fUseCount;
|
||||
|
||||
mutex fLock;
|
||||
|
||||
LockOwner* fNext;
|
||||
LockOwner* fPrev;
|
||||
|
||||
LockOwner(uint32 owner);
|
||||
~LockOwner();
|
||||
};
|
||||
|
||||
struct LockInfo {
|
||||
LockOwner* fOwner;
|
||||
|
||||
uint64 fStart;
|
||||
uint64 fLength;
|
||||
LockType fType;
|
||||
|
||||
LockInfo* fNext;
|
||||
LockInfo* fCookieNext;
|
||||
|
||||
LockInfo(LockOwner* owner);
|
||||
~LockInfo();
|
||||
|
||||
bool operator==(const struct flock& lock) const;
|
||||
bool operator==(const LockInfo& lock) const;
|
||||
};
|
||||
|
||||
struct Cookie {
|
||||
struct RequestEntry {
|
||||
RPC::Request* fRequest;
|
||||
RequestEntry* fNext;
|
||||
};
|
||||
|
||||
FileSystem* fFileSystem;
|
||||
RequestEntry* fRequests;
|
||||
mutex fRequestLock;
|
||||
|
||||
sem_id fSnoozeCancel;
|
||||
|
||||
Cookie();
|
||||
virtual ~Cookie();
|
||||
|
||||
status_t RegisterRequest(RPC::Request* req);
|
||||
status_t UnregisterRequest(RPC::Request* req);
|
||||
status_t CancelAll();
|
||||
};
|
||||
|
||||
struct OpenStateCookie : public Cookie {
|
||||
OpenState* fOpenState;
|
||||
uint32 fMode;
|
||||
};
|
||||
|
||||
struct OpenFileCookie : public OpenStateCookie {
|
||||
LockInfo* fLocks;
|
||||
|
||||
void AddLock(LockInfo* lock);
|
||||
void RemoveLock(LockInfo* lock, LockInfo* prev);
|
||||
|
||||
OpenFileCookie();
|
||||
};
|
||||
|
||||
struct OpenDirCookie : public Cookie {
|
||||
int fSpecial;
|
||||
DirectoryCacheSnapshot* fSnapshot;
|
||||
NameCacheEntry* fCurrent;
|
||||
bool fEOF;
|
||||
|
||||
bool fAttrDir;
|
||||
|
||||
~OpenDirCookie();
|
||||
};
|
||||
|
||||
struct OpenAttrCookie : public OpenStateCookie { };
|
||||
|
||||
|
||||
#endif // COOKIE_H
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "Delegation.h"
|
||||
|
||||
#include "Inode.h"
|
||||
#include "Request.h"
|
||||
|
||||
|
||||
Delegation::Delegation(const OpenDelegationData& data, Inode* inode,
|
||||
uint64 clientID, bool attribute)
|
||||
:
|
||||
fClientID(clientID),
|
||||
fData(data),
|
||||
fInode(inode),
|
||||
fAttribute(attribute)
|
||||
{
|
||||
ASSERT(inode != NULL);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Delegation::GiveUp(bool truncate)
|
||||
{
|
||||
if (!fAttribute && !truncate)
|
||||
fInode->SyncAndCommit(true);
|
||||
|
||||
ReturnDelegation();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Delegation::ReturnDelegation()
|
||||
{
|
||||
do {
|
||||
RPC::Server* serv = fFileSystem->Server();
|
||||
Request request(serv, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
req.DelegReturn(fData.fStateID, fData.fStateSeq);
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), serv, NULL, fInode->GetOpenState()))
|
||||
continue;
|
||||
|
||||
reply.PutFH();
|
||||
|
||||
return reply.DelegReturn();
|
||||
} while (true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef DELEGATION_H
|
||||
#define DELEGATION_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "NFS4Object.h"
|
||||
|
||||
|
||||
class Inode;
|
||||
|
||||
class Delegation : public NFS4Object,
|
||||
public DoublyLinkedListLinkImpl<Delegation> {
|
||||
public:
|
||||
Delegation(const OpenDelegationData& data, Inode* inode,
|
||||
uint64 clientID, bool attr = false);
|
||||
|
||||
status_t GiveUp(bool truncate = false);
|
||||
|
||||
inline void SetData(const OpenDelegationData& data);
|
||||
inline Inode* GetInode();
|
||||
inline OpenDelegation Type();
|
||||
|
||||
protected:
|
||||
status_t ReturnDelegation();
|
||||
|
||||
private:
|
||||
uint64 fClientID;
|
||||
OpenDelegationData fData;
|
||||
Inode* fInode;
|
||||
bool fAttribute;
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
Delegation::SetData(const OpenDelegationData& data)
|
||||
{
|
||||
fData = data;
|
||||
}
|
||||
|
||||
|
||||
inline Inode*
|
||||
Delegation::GetInode()
|
||||
{
|
||||
return fInode;
|
||||
}
|
||||
|
||||
|
||||
inline OpenDelegation
|
||||
Delegation::Type()
|
||||
{
|
||||
return fData.fType;
|
||||
}
|
||||
|
||||
|
||||
#endif // DELEGATION_H
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "DirectoryCache.h"
|
||||
|
||||
#include <fs_cache.h>
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
|
||||
NameCacheEntry::NameCacheEntry(const char* name, ino_t node)
|
||||
:
|
||||
fNode(node),
|
||||
fName(strdup(name))
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
}
|
||||
|
||||
|
||||
NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry)
|
||||
:
|
||||
fNode(entry.fNode),
|
||||
fName(strdup(entry.fName))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NameCacheEntry::~NameCacheEntry()
|
||||
{
|
||||
free(const_cast<char*>(fName));
|
||||
}
|
||||
|
||||
|
||||
DirectoryCacheSnapshot::DirectoryCacheSnapshot()
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
DirectoryCacheSnapshot::DirectoryCacheSnapshot(
|
||||
const DirectoryCacheSnapshot& snapshot)
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
|
||||
MutexLocker _(snapshot.fLock);
|
||||
NameCacheEntry* entry = snapshot.fEntries.Head();
|
||||
NameCacheEntry* new_entry;
|
||||
while (entry) {
|
||||
new_entry = new NameCacheEntry(*entry);
|
||||
if (new_entry == NULL)
|
||||
break;
|
||||
|
||||
fEntries.Add(new_entry);
|
||||
|
||||
entry = snapshot.fEntries.GetNext(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DirectoryCacheSnapshot::~DirectoryCacheSnapshot()
|
||||
{
|
||||
while (!fEntries.IsEmpty()) {
|
||||
NameCacheEntry* current = fEntries.RemoveHead();
|
||||
delete current;
|
||||
}
|
||||
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
DirectoryCache::DirectoryCache(Inode* inode, bool attr)
|
||||
:
|
||||
fRevalidated(false),
|
||||
fDirectoryCache(NULL),
|
||||
fInode(inode),
|
||||
fAttrDir(attr),
|
||||
fTrashed(true)
|
||||
{
|
||||
ASSERT(inode != NULL);
|
||||
|
||||
mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
DirectoryCache::~DirectoryCache()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DirectoryCache::Reset()
|
||||
{
|
||||
Trash();
|
||||
fExpireTime = system_time() + kExpirationTime;
|
||||
fTrashed = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DirectoryCache::Trash()
|
||||
{
|
||||
while (!fNameCache.IsEmpty()) {
|
||||
NameCacheEntry* current = fNameCache.RemoveHead();
|
||||
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
|
||||
current->fName);
|
||||
delete current;
|
||||
}
|
||||
|
||||
_SetSnapshot(NULL);
|
||||
|
||||
fTrashed = true;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryCache::AddEntry(const char* name, ino_t node, bool created)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
|
||||
NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
|
||||
if (entry == NULL)
|
||||
return B_NO_MEMORY;
|
||||
if (entry->fName == NULL) {
|
||||
delete entry;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
fNameCache.Add(entry);
|
||||
|
||||
if (created && fDirectoryCache != NULL) {
|
||||
MutexLocker _(fDirectoryCache->fLock);
|
||||
NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
|
||||
if (entry == NULL)
|
||||
return B_NO_MEMORY;
|
||||
if (entry->fName == NULL) {
|
||||
delete entry;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
fDirectoryCache->fEntries.Add(entry);
|
||||
}
|
||||
|
||||
if (!fAttrDir) {
|
||||
return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(),
|
||||
name, node);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DirectoryCache::RemoveEntry(const char* name)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
|
||||
SinglyLinkedList<NameCacheEntry>::Iterator iterator
|
||||
= fNameCache.GetIterator();
|
||||
NameCacheEntry* previous = NULL;
|
||||
NameCacheEntry* current = iterator.Next();
|
||||
while (current != NULL) {
|
||||
if (strcmp(current->fName, name) == 0) {
|
||||
fNameCache.Remove(previous, current);
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
current = iterator.Next();
|
||||
}
|
||||
|
||||
if (fDirectoryCache != NULL) {
|
||||
MutexLocker _(fDirectoryCache->fLock);
|
||||
iterator = fDirectoryCache->fEntries.GetIterator();
|
||||
previous = NULL;
|
||||
current = iterator.Next();
|
||||
while (current != NULL) {
|
||||
if (strcmp(current->fName, name) == 0) {
|
||||
fDirectoryCache->fEntries.Remove(previous, current);
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
current = iterator.Next();
|
||||
}
|
||||
}
|
||||
|
||||
if (!fAttrDir) {
|
||||
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DirectoryCache::_SetSnapshot(DirectoryCacheSnapshot* snapshot)
|
||||
{
|
||||
if (fDirectoryCache != NULL)
|
||||
fDirectoryCache->ReleaseReference();
|
||||
fDirectoryCache = snapshot;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryCache::_LoadSnapshot(bool trash)
|
||||
{
|
||||
DirectoryCacheSnapshot* oldSnapshot = fDirectoryCache;
|
||||
if (oldSnapshot != NULL)
|
||||
oldSnapshot->AcquireReference();
|
||||
|
||||
if (trash)
|
||||
Trash();
|
||||
|
||||
DirectoryCacheSnapshot* newSnapshot;
|
||||
status_t result = fInode->GetDirSnapshot(&newSnapshot, NULL, &fChange,
|
||||
fAttrDir);
|
||||
if (result != B_OK) {
|
||||
if (oldSnapshot != NULL)
|
||||
oldSnapshot->ReleaseReference();
|
||||
return result;
|
||||
}
|
||||
newSnapshot->AcquireReference();
|
||||
|
||||
_SetSnapshot(newSnapshot);
|
||||
fExpireTime = system_time() + kExpirationTime;
|
||||
|
||||
fTrashed = false;
|
||||
|
||||
if (oldSnapshot != NULL)
|
||||
NotifyChanges(oldSnapshot, newSnapshot);
|
||||
|
||||
if (oldSnapshot != NULL)
|
||||
oldSnapshot->ReleaseReference();
|
||||
|
||||
newSnapshot->ReleaseReference();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryCache::Revalidate()
|
||||
{
|
||||
if (fExpireTime < system_time())
|
||||
return B_OK;
|
||||
|
||||
uint64 change;
|
||||
if (fInode->GetChangeInfo(&change, fAttrDir) != B_OK) {
|
||||
Trash();
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (change == fChange) {
|
||||
fExpireTime = system_time() + kExpirationTime;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return _LoadSnapshot(true);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
|
||||
DirectoryCacheSnapshot* newSnapshot)
|
||||
{
|
||||
ASSERT(newSnapshot != NULL);
|
||||
ASSERT(oldSnapshot != NULL);
|
||||
|
||||
MutexLocker _(newSnapshot->fLock);
|
||||
|
||||
SinglyLinkedList<NameCacheEntry>::Iterator oldIt
|
||||
= oldSnapshot->fEntries.GetIterator();
|
||||
NameCacheEntry* oldCurrent;
|
||||
|
||||
SinglyLinkedList<NameCacheEntry>::Iterator newIt
|
||||
= newSnapshot->fEntries.GetIterator();
|
||||
NameCacheEntry* newCurrent = newIt.Next();
|
||||
while (newCurrent != NULL) {
|
||||
oldIt = oldSnapshot->fEntries.GetIterator();
|
||||
oldCurrent = oldIt.Next();
|
||||
|
||||
bool found = false;
|
||||
NameCacheEntry* prev = NULL;
|
||||
while (oldCurrent != NULL) {
|
||||
if (oldCurrent->fNode == newCurrent->fNode
|
||||
&& strcmp(oldCurrent->fName, newCurrent->fName) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
prev = oldCurrent;
|
||||
oldCurrent = oldIt.Next();
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
if (fAttrDir) {
|
||||
notify_attribute_changed(fInode->GetFileSystem()->DevId(),
|
||||
fInode->ID(), newCurrent->fName, B_ATTR_CREATED);
|
||||
} else {
|
||||
notify_entry_created(fInode->GetFileSystem()->DevId(),
|
||||
fInode->ID(), newCurrent->fName, newCurrent->fNode);
|
||||
|
||||
do {
|
||||
FileInfo fi;
|
||||
fi.fFileId = newCurrent->fNode;
|
||||
fi.fParent = fInode->fInfo.fHandle;
|
||||
status_t result = fi.CreateName(fInode->fInfo.fPath,
|
||||
newCurrent->fName);
|
||||
if (result != B_OK)
|
||||
break;
|
||||
|
||||
fInode->GetFileSystem()->InoIdMap()->AddEntry(fi,
|
||||
Inode::FileIdToInoT(newCurrent->fNode), true);
|
||||
} while (false);
|
||||
}
|
||||
} else
|
||||
oldSnapshot->fEntries.Remove(prev, oldCurrent);
|
||||
|
||||
newCurrent = newIt.Next();
|
||||
}
|
||||
|
||||
oldIt = oldSnapshot->fEntries.GetIterator();
|
||||
oldCurrent = oldIt.Next();
|
||||
|
||||
while (oldCurrent != NULL) {
|
||||
if (fAttrDir) {
|
||||
notify_attribute_changed(fInode->GetFileSystem()->DevId(),
|
||||
fInode->ID(), newCurrent->fName, B_ATTR_REMOVED);
|
||||
} else {
|
||||
notify_entry_removed(fInode->GetFileSystem()->DevId(), fInode->ID(),
|
||||
oldCurrent->fName, oldCurrent->fNode);
|
||||
}
|
||||
oldCurrent = oldIt.Next();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef DIRECTORYCACHE_H
|
||||
#define DIRECTORYCACHE_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/KernelReferenceable.h>
|
||||
#include <util/SinglyLinkedList.h>
|
||||
|
||||
|
||||
class Inode;
|
||||
|
||||
struct NameCacheEntry :
|
||||
public SinglyLinkedListLinkImpl<NameCacheEntry> {
|
||||
ino_t fNode;
|
||||
const char* fName;
|
||||
|
||||
NameCacheEntry(const char* name, ino_t node);
|
||||
NameCacheEntry(const NameCacheEntry& entry);
|
||||
~NameCacheEntry();
|
||||
};
|
||||
|
||||
struct DirectoryCacheSnapshot : public KernelReferenceable {
|
||||
SinglyLinkedList<NameCacheEntry> fEntries;
|
||||
mutable mutex fLock;
|
||||
|
||||
DirectoryCacheSnapshot();
|
||||
DirectoryCacheSnapshot(
|
||||
const DirectoryCacheSnapshot& snapshot);
|
||||
~DirectoryCacheSnapshot();
|
||||
};
|
||||
|
||||
class DirectoryCache {
|
||||
public:
|
||||
DirectoryCache(Inode* inode, bool attr = false);
|
||||
~DirectoryCache();
|
||||
|
||||
inline void Lock();
|
||||
inline void Unlock();
|
||||
|
||||
void Reset();
|
||||
void Trash();
|
||||
inline bool Valid();
|
||||
|
||||
status_t AddEntry(const char* name, ino_t node,
|
||||
bool created = false);
|
||||
void RemoveEntry(const char* name);
|
||||
|
||||
inline status_t GetSnapshot(DirectoryCacheSnapshot** snapshot);
|
||||
|
||||
inline SinglyLinkedList<NameCacheEntry>& EntriesList();
|
||||
|
||||
status_t Revalidate();
|
||||
|
||||
inline status_t ValidateChangeInfo(uint64 change);
|
||||
inline void SetChangeInfo(uint64 change);
|
||||
inline uint64 ChangeInfo();
|
||||
|
||||
inline Inode* GetInode();
|
||||
|
||||
static const bigtime_t kExpirationTime = 15000000;
|
||||
|
||||
bool fRevalidated;
|
||||
protected:
|
||||
void NotifyChanges(DirectoryCacheSnapshot* oldSnapshot,
|
||||
DirectoryCacheSnapshot* newSnapshot);
|
||||
|
||||
private:
|
||||
void _SetSnapshot(DirectoryCacheSnapshot* snapshot);
|
||||
status_t _LoadSnapshot(bool trash);
|
||||
|
||||
SinglyLinkedList<NameCacheEntry> fNameCache;
|
||||
|
||||
DirectoryCacheSnapshot* fDirectoryCache;
|
||||
|
||||
Inode* fInode;
|
||||
|
||||
bool fAttrDir;
|
||||
bool fTrashed;
|
||||
mutex fLock;
|
||||
|
||||
uint64 fChange;
|
||||
bigtime_t fExpireTime;
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
DirectoryCache::Lock()
|
||||
{
|
||||
mutex_lock(&fLock);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
DirectoryCache::Unlock()
|
||||
{
|
||||
mutex_unlock(&fLock);
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
DirectoryCache::Valid()
|
||||
{
|
||||
return !fTrashed;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
DirectoryCache::GetSnapshot(DirectoryCacheSnapshot** snapshot)
|
||||
{
|
||||
ASSERT(snapshot != NULL);
|
||||
|
||||
status_t result = B_OK;
|
||||
if (fDirectoryCache == NULL)
|
||||
result = _LoadSnapshot(false);
|
||||
*snapshot = fDirectoryCache;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
inline SinglyLinkedList<NameCacheEntry>&
|
||||
DirectoryCache::EntriesList()
|
||||
{
|
||||
return fNameCache;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
DirectoryCache::ValidateChangeInfo(uint64 change)
|
||||
{
|
||||
if (fTrashed || change != fChange) {
|
||||
Trash();
|
||||
fChange = change;
|
||||
fExpireTime = system_time() + kExpirationTime;
|
||||
fTrashed = false;
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
DirectoryCache::SetChangeInfo(uint64 change)
|
||||
{
|
||||
fExpireTime = system_time() + kExpirationTime;
|
||||
fChange = change;
|
||||
}
|
||||
|
||||
|
||||
inline uint64
|
||||
DirectoryCache::ChangeInfo()
|
||||
{
|
||||
return fChange;
|
||||
}
|
||||
|
||||
|
||||
inline Inode*
|
||||
DirectoryCache::GetInode()
|
||||
{
|
||||
return fInode;
|
||||
}
|
||||
|
||||
|
||||
#endif // DIRECTORYCACHE_H
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "FileInfo.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Request.h"
|
||||
|
||||
|
||||
status_t
|
||||
FileInfo::ParsePath(RequestBuilder& req, uint32& count, const char* _path)
|
||||
{
|
||||
ASSERT(_path != NULL);
|
||||
|
||||
char* path = strdup(_path);
|
||||
if (path == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
char* pathStart = path;
|
||||
char* pathEnd;
|
||||
|
||||
while (pathStart != NULL) {
|
||||
pathEnd = strchr(pathStart, '/');
|
||||
if (pathEnd != NULL)
|
||||
*pathEnd = '\0';
|
||||
|
||||
if (pathEnd != pathStart) {
|
||||
if (!strcmp(pathStart, "..")) {
|
||||
req.LookUpUp();
|
||||
count++;
|
||||
} else if (strcmp(pathStart, ".")) {
|
||||
req.LookUp(pathStart);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pathEnd != NULL && pathEnd[1] != '\0')
|
||||
pathStart = pathEnd + 1;
|
||||
else
|
||||
pathStart = NULL;
|
||||
}
|
||||
free(path);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FileInfo::CreateName(const char* dirPath, const char* name)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
|
||||
free(const_cast<char*>(fName));
|
||||
fName = strdup(name);
|
||||
if (fName == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
free(const_cast<char*>(fPath));
|
||||
fPath = NULL;
|
||||
if (dirPath != NULL) {
|
||||
char* path = reinterpret_cast<char*>(malloc(strlen(name) + 2
|
||||
+ strlen(dirPath)));
|
||||
if (path == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
strcpy(path, dirPath);
|
||||
strcat(path, "/");
|
||||
strcat(path, name);
|
||||
|
||||
fPath = path;
|
||||
} else
|
||||
fPath = strdup(name);
|
||||
|
||||
if (fPath == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FileInfo::UpdateFileHandles(FileSystem* fs)
|
||||
{
|
||||
ASSERT(fs != NULL);
|
||||
|
||||
Request request(fs->Server(), fs);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutRootFH();
|
||||
|
||||
uint32 lookupCount = 0;
|
||||
status_t result;
|
||||
|
||||
result = ParsePath(req, lookupCount, fs->Path());
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
result = ParsePath(req, lookupCount, fPath);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if (fs->IsAttrSupported(FATTR4_FILEID)) {
|
||||
AttrValue attr;
|
||||
attr.fAttribute = FATTR4_FILEID;
|
||||
attr.fFreePointer = false;
|
||||
attr.fData.fValue64 = fFileId;
|
||||
req.Verify(&attr, 1);
|
||||
}
|
||||
|
||||
req.GetFH();
|
||||
req.LookUpUp();
|
||||
req.GetFH();
|
||||
|
||||
result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
reply.PutRootFH();
|
||||
for (uint32 i = 0; i < lookupCount; i++)
|
||||
reply.LookUp();
|
||||
|
||||
if (fs->IsAttrSupported(FATTR4_FILEID)) {
|
||||
result = reply.Verify();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
reply.GetFH(&fHandle);
|
||||
if (reply.LookUpUp() == B_ENTRY_NOT_FOUND) {
|
||||
fParent = fHandle;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return reply.GetFH(&fParent);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef FILEINFO_H
|
||||
#define FILEINFO_H
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
#define NFS4_FHSIZE 128
|
||||
|
||||
struct FileHandle {
|
||||
uint8 fSize;
|
||||
uint8 fData[NFS4_FHSIZE];
|
||||
|
||||
inline FileHandle();
|
||||
inline FileHandle(const FileHandle& fh);
|
||||
inline FileHandle& operator=(const FileHandle& fh);
|
||||
|
||||
|
||||
inline bool operator!=(const FileHandle& handle) const;
|
||||
inline bool operator>(const FileHandle& handle) const;
|
||||
inline bool operator<(const FileHandle& handle) const;
|
||||
};
|
||||
|
||||
|
||||
class FileSystem;
|
||||
class RequestBuilder;
|
||||
|
||||
// Complete information needed to identify a file in any situation.
|
||||
// Unfortunately just a FileHandle is not enough even when they are persistent
|
||||
// since OPEN requires both parent FileHandle and file name (just like LOOKUP).
|
||||
struct FileInfo {
|
||||
uint64 fFileId;
|
||||
FileHandle fHandle;
|
||||
|
||||
FileHandle fParent;
|
||||
const char* fName;
|
||||
const char* fPath;
|
||||
|
||||
FileHandle fAttrDir;
|
||||
|
||||
inline FileInfo();
|
||||
inline ~FileInfo();
|
||||
inline FileInfo(const FileInfo& fi);
|
||||
inline FileInfo& operator=(const FileInfo& fi);
|
||||
|
||||
status_t UpdateFileHandles(FileSystem* fs);
|
||||
|
||||
static status_t ParsePath(RequestBuilder& req, uint32& count,
|
||||
const char* _path);
|
||||
|
||||
status_t CreateName(const char* dirPath, const char* name);
|
||||
};
|
||||
|
||||
struct FileSystemId {
|
||||
uint64 fMajor;
|
||||
uint64 fMinor;
|
||||
|
||||
inline bool operator==(const FileSystemId& fsid) const;
|
||||
inline bool operator!=(const FileSystemId& fsid) const;
|
||||
};
|
||||
|
||||
|
||||
inline
|
||||
FileHandle::FileHandle()
|
||||
:
|
||||
fSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
FileHandle::FileHandle(const FileHandle& fh)
|
||||
:
|
||||
fSize(fh.fSize)
|
||||
{
|
||||
memcpy(fData, fh.fData, fSize);
|
||||
}
|
||||
|
||||
|
||||
inline FileHandle&
|
||||
FileHandle::operator=(const FileHandle& fh)
|
||||
{
|
||||
fSize = fh.fSize;
|
||||
memcpy(fData, fh.fData, fSize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
FileHandle::operator!=(const FileHandle& handle) const
|
||||
{
|
||||
if (fSize != handle.fSize)
|
||||
return true;
|
||||
return memcmp(fData, handle.fData, fSize) != 0;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
FileHandle::operator>(const FileHandle& handle) const
|
||||
{
|
||||
if (fSize > handle.fSize)
|
||||
return true;
|
||||
return memcmp(fData, handle.fData, fSize) > 0;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
FileHandle::operator<(const FileHandle& handle) const
|
||||
{
|
||||
if (fSize < handle.fSize)
|
||||
return true;
|
||||
return memcmp(fData, handle.fData, fSize) < 0;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
FileInfo::FileInfo()
|
||||
:
|
||||
fFileId(0),
|
||||
fName(NULL),
|
||||
fPath(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
FileInfo::~FileInfo()
|
||||
{
|
||||
free(const_cast<char*>(fName));
|
||||
free(const_cast<char*>(fPath));
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
FileInfo::FileInfo(const FileInfo& fi)
|
||||
:
|
||||
fFileId(fi.fFileId),
|
||||
fHandle(fi.fHandle),
|
||||
fParent(fi.fParent),
|
||||
fName(strdup(fi.fName)),
|
||||
fPath(strdup(fi.fPath))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline FileInfo&
|
||||
FileInfo::operator=(const FileInfo& fi)
|
||||
{
|
||||
fFileId = fi.fFileId;
|
||||
fHandle = fi.fHandle;
|
||||
fParent = fi.fParent;
|
||||
|
||||
free(const_cast<char*>(fName));
|
||||
fName = strdup(fi.fName);
|
||||
|
||||
free(const_cast<char*>(fPath));
|
||||
fPath = strdup(fi.fPath);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
FileSystemId::operator==(const FileSystemId& fsid) const
|
||||
{
|
||||
return fMajor == fsid.fMajor && fMinor == fsid.fMinor;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
FileSystemId::operator!=(const FileSystemId& fsid) const
|
||||
{
|
||||
return !operator==(fsid);
|
||||
}
|
||||
|
||||
|
||||
#endif // FILEHINFO_H
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <lock.h>
|
||||
|
||||
#include "Request.h"
|
||||
#include "RootInode.h"
|
||||
|
||||
|
||||
extern RPC::ServerManager* gRPCServerManager;
|
||||
extern RPC::ProgramData* CreateNFS4Server(RPC::Server* serv);
|
||||
|
||||
|
||||
FileSystem::FileSystem(const MountConfiguration& configuration)
|
||||
:
|
||||
fOpenCount(0),
|
||||
fOpenOwnerSequence(0),
|
||||
fNamedAttrs(true),
|
||||
fPath(NULL),
|
||||
fRoot(NULL),
|
||||
fId(1),
|
||||
fConfiguration(configuration)
|
||||
{
|
||||
fOpenOwner = rand();
|
||||
fOpenOwner <<= 32;
|
||||
fOpenOwner |= rand();
|
||||
|
||||
mutex_init(&fOpenOwnerLock, NULL);
|
||||
mutex_init(&fOpenLock, NULL);
|
||||
mutex_init(&fDelegationLock, NULL);
|
||||
mutex_init(&fCreateFileLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
FileSystem::~FileSystem()
|
||||
{
|
||||
NFS4Server* server = reinterpret_cast<NFS4Server*>(fServer->PrivateData());
|
||||
if (server != NULL)
|
||||
server->RemoveFileSystem(this);
|
||||
|
||||
mutex_destroy(&fDelegationLock);
|
||||
mutex_destroy(&fOpenLock);
|
||||
mutex_destroy(&fOpenOwnerLock);
|
||||
mutex_destroy(&fCreateFileLock);
|
||||
|
||||
free(const_cast<char*>(fPath));
|
||||
delete fRoot;
|
||||
}
|
||||
|
||||
|
||||
static const char*
|
||||
GetPath(const char* root, const char* path)
|
||||
{
|
||||
ASSERT(path != NULL);
|
||||
|
||||
int slash = 0;
|
||||
int i;
|
||||
for (i = 0; path[i] != '\0'; i++) {
|
||||
if (path[i] == '/')
|
||||
slash = i;
|
||||
|
||||
if (root == NULL)
|
||||
break;
|
||||
|
||||
if (path[i] != root[i] || root[i] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
if (path[i] == '\0')
|
||||
return NULL;
|
||||
|
||||
return path + slash;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FileSystem::Mount(FileSystem** _fs, RPC::Server* serv, const char* fsPath,
|
||||
dev_t id, const MountConfiguration& configuration)
|
||||
{
|
||||
ASSERT(_fs != NULL);
|
||||
ASSERT(serv != NULL);
|
||||
ASSERT(fsPath != NULL);
|
||||
|
||||
FileSystem* fs = new(std::nothrow) FileSystem(configuration);
|
||||
if (fs == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ObjectDeleter<FileSystem> fsDeleter(fs);
|
||||
|
||||
Request request(serv, fs);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutRootFH();
|
||||
|
||||
uint32 lookupCount = 0;
|
||||
status_t result = FileInfo::ParsePath(req, lookupCount, fsPath);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
req.GetFH();
|
||||
req.Access();
|
||||
|
||||
Attribute attr[] = { FATTR4_SUPPORTED_ATTRS, FATTR4_FH_EXPIRE_TYPE,
|
||||
FATTR4_FSID, FATTR4_FS_LOCATIONS };
|
||||
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
|
||||
|
||||
result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
reply.PutRootFH();
|
||||
|
||||
for (uint32 i = 0; i < lookupCount; i++)
|
||||
reply.LookUp();
|
||||
|
||||
FileHandle fh;
|
||||
reply.GetFH(&fh);
|
||||
|
||||
uint32 allowed;
|
||||
result = reply.Access(NULL, &allowed);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
else if ((allowed & (ACCESS4_READ | ACCESS4_LOOKUP))
|
||||
!= (ACCESS4_READ | ACCESS4_LOOKUP))
|
||||
return B_PERMISSION_DENIED;
|
||||
|
||||
AttrValue* values;
|
||||
uint32 count;
|
||||
result = reply.GetAttr(&values, &count);
|
||||
if (result != B_OK || count < 2)
|
||||
return result;
|
||||
|
||||
// FATTR4_SUPPORTED_ATTRS is mandatory
|
||||
memcpy(fs->fSupAttrs, &values[0].fData.fValue64, sizeof(fs->fSupAttrs));
|
||||
|
||||
// FATTR4_FH_EXPIRE_TYPE is mandatory
|
||||
fs->fExpireType = values[1].fData.fValue32;
|
||||
|
||||
// FATTR4_FSID is mandatory
|
||||
FileSystemId* fsid
|
||||
= reinterpret_cast<FileSystemId*>(values[2].fData.fPointer);
|
||||
|
||||
if (count == 4 && values[3].fAttribute == FATTR4_FS_LOCATIONS) {
|
||||
FSLocations* locs
|
||||
= reinterpret_cast<FSLocations*>(values[3].fData.fLocations);
|
||||
|
||||
fs->fPath = strdup(locs->fRootPath);
|
||||
} else
|
||||
fs->fPath = NULL;
|
||||
|
||||
FileInfo fi;
|
||||
const char* name;
|
||||
if (fsPath != NULL && fsPath[0] == '/')
|
||||
fsPath++;
|
||||
|
||||
fs->fServer = serv;
|
||||
fs->fDevId = id;
|
||||
fs->fFsId = *fsid;
|
||||
|
||||
fi.fHandle = fh;
|
||||
fi.fParent = fh;
|
||||
fi.fPath = strdup(GetPath(fs->fPath, fsPath));
|
||||
|
||||
if (fi.fPath != NULL) {
|
||||
name = strrchr(fi.fPath, '/');
|
||||
if (name != NULL) {
|
||||
name++;
|
||||
fi.fName = strdup(name);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] values;
|
||||
|
||||
Inode* inode;
|
||||
result = Inode::CreateInode(fs, fi, &inode);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
name = strrchr(fsPath, '/');
|
||||
if (name != NULL) {
|
||||
name++;
|
||||
reinterpret_cast<RootInode*>(inode)->SetName(name);
|
||||
} else if (fsPath[0] != '\0')
|
||||
reinterpret_cast<RootInode*>(inode)->SetName(fsPath);
|
||||
else {
|
||||
char* address = serv->ID().UniversalAddress();
|
||||
if (address != NULL)
|
||||
reinterpret_cast<RootInode*>(inode)->SetName(address);
|
||||
else
|
||||
reinterpret_cast<RootInode*>(inode)->SetName("NFS4 Share");
|
||||
free(address);
|
||||
}
|
||||
|
||||
fs->fRoot = reinterpret_cast<RootInode*>(inode);
|
||||
|
||||
fs->NFSServer()->AddFileSystem(fs);
|
||||
*_fs = fs;
|
||||
|
||||
fsDeleter.Detach();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FileSystem::GetInode(ino_t id, Inode** _inode)
|
||||
{
|
||||
ASSERT(_inode != NULL);
|
||||
|
||||
FileInfo fi;
|
||||
status_t result = fInoIdMap.GetFileInfo(&fi, id);
|
||||
ASSERT(result != B_ENTRY_NOT_FOUND);
|
||||
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
Inode* inode;
|
||||
result = Inode::CreateInode(this, fi, &inode);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
*_inode = inode;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FileSystem::Migrate(const RPC::Server* serv)
|
||||
{
|
||||
ASSERT(serv != NULL);
|
||||
|
||||
MutexLocker _(fOpenLock);
|
||||
if (serv != fServer)
|
||||
return B_OK;
|
||||
|
||||
if (!fRoot->ProbeMigration())
|
||||
return B_OK;
|
||||
|
||||
AttrValue* values;
|
||||
status_t result = fRoot->GetLocations(&values);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
FSLocations* locs
|
||||
= reinterpret_cast<FSLocations*>(values[0].fData.fLocations);
|
||||
|
||||
RPC::Server* server = fServer;
|
||||
for (uint32 i = 0; i < locs->fCount; i++) {
|
||||
for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) {
|
||||
AddressResolver resolver(locs->fLocations[i].fLocations[j]);
|
||||
|
||||
if (gRPCServerManager->Acquire(&fServer, &resolver,
|
||||
CreateNFS4Server) == B_OK) {
|
||||
|
||||
free(const_cast<char*>(fPath));
|
||||
fPath = strdup(locs->fLocations[i].fRootPath);
|
||||
|
||||
if (fPath == NULL) {
|
||||
gRPCServerManager->Release(fServer);
|
||||
fServer = server;
|
||||
|
||||
delete[] values;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] values;
|
||||
|
||||
if (server == fServer) {
|
||||
gRPCServerManager->Release(server);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
NFS4Server* old = reinterpret_cast<NFS4Server*>(server->PrivateData());
|
||||
old->RemoveFileSystem(this);
|
||||
NFSServer()->AddFileSystem(this);
|
||||
|
||||
gRPCServerManager->Release(server);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
DoublyLinkedList<OpenState>&
|
||||
FileSystem::OpenFilesLock()
|
||||
{
|
||||
mutex_lock(&fOpenLock);
|
||||
return fOpenFiles;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FileSystem::OpenFilesUnlock()
|
||||
{
|
||||
mutex_unlock(&fOpenLock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FileSystem::AddOpenFile(OpenState* state)
|
||||
{
|
||||
ASSERT(state != NULL);
|
||||
|
||||
MutexLocker _(fOpenLock);
|
||||
|
||||
fOpenFiles.InsertBefore(fOpenFiles.Head(), state);
|
||||
|
||||
NFSServer()->IncUsage();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FileSystem::RemoveOpenFile(OpenState* state)
|
||||
{
|
||||
ASSERT(state != NULL);
|
||||
|
||||
MutexLocker _(fOpenLock);
|
||||
|
||||
fOpenFiles.Remove(state);
|
||||
|
||||
NFSServer()->DecUsage();
|
||||
}
|
||||
|
||||
|
||||
DoublyLinkedList<Delegation>&
|
||||
FileSystem::DelegationsLock()
|
||||
{
|
||||
mutex_lock(&fDelegationLock);
|
||||
return fDelegationList;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FileSystem::DelegationsUnlock()
|
||||
{
|
||||
mutex_unlock(&fDelegationLock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FileSystem::AddDelegation(Delegation* delegation)
|
||||
{
|
||||
ASSERT(delegation != NULL);
|
||||
|
||||
MutexLocker _(fDelegationLock);
|
||||
|
||||
fDelegationList.InsertBefore(fDelegationList.Head(), delegation);
|
||||
|
||||
fHandleToDelegation.Remove(delegation->fInfo.fHandle);
|
||||
fHandleToDelegation.Insert(delegation->fInfo.fHandle, delegation);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FileSystem::RemoveDelegation(Delegation* delegation)
|
||||
{
|
||||
ASSERT(delegation != NULL);
|
||||
|
||||
MutexLocker _(fDelegationLock);
|
||||
|
||||
fDelegationList.Remove(delegation);
|
||||
fHandleToDelegation.Remove(delegation->fInfo.fHandle);
|
||||
}
|
||||
|
||||
|
||||
Delegation*
|
||||
FileSystem::GetDelegation(const FileHandle& handle)
|
||||
{
|
||||
MutexLocker _(fDelegationLock);
|
||||
|
||||
AVLTreeMap<FileHandle, Delegation*>::Iterator it;
|
||||
it = fHandleToDelegation.Find(handle);
|
||||
if (!it.HasCurrent())
|
||||
return NULL;
|
||||
|
||||
return it.Current();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef FILESYSTEM_H
|
||||
#define FILESYSTEM_H
|
||||
|
||||
|
||||
#include "Delegation.h"
|
||||
#include "InodeIdMap.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "NFS4Server.h"
|
||||
|
||||
|
||||
class Inode;
|
||||
class RootInode;
|
||||
|
||||
struct MountConfiguration {
|
||||
bool fHard;
|
||||
int fRetryLimit;
|
||||
bigtime_t fRequestTimeout;
|
||||
|
||||
bool fEmulateNamedAttrs;
|
||||
bool fCacheMetadata;
|
||||
};
|
||||
|
||||
class FileSystem : public DoublyLinkedListLinkImpl<FileSystem> {
|
||||
public:
|
||||
static status_t Mount(FileSystem** pfs, RPC::Server* serv,
|
||||
const char* path, dev_t id,
|
||||
const MountConfiguration& configuration);
|
||||
~FileSystem();
|
||||
|
||||
status_t GetInode(ino_t id, Inode** inode);
|
||||
inline RootInode* Root();
|
||||
|
||||
status_t Migrate(const RPC::Server* serv);
|
||||
|
||||
DoublyLinkedList<OpenState>& OpenFilesLock();
|
||||
void OpenFilesUnlock();
|
||||
inline uint32 OpenFilesCount();
|
||||
void AddOpenFile(OpenState* state);
|
||||
void RemoveOpenFile(OpenState* state);
|
||||
|
||||
DoublyLinkedList<Delegation>& DelegationsLock();
|
||||
void DelegationsUnlock();
|
||||
void AddDelegation(Delegation* delegation);
|
||||
void RemoveDelegation(Delegation* delegation);
|
||||
Delegation* GetDelegation(const FileHandle& handle);
|
||||
|
||||
inline bool IsAttrSupported(Attribute attr) const;
|
||||
inline uint32 ExpireType() const;
|
||||
|
||||
inline RPC::Server* Server();
|
||||
inline NFS4Server* NFSServer();
|
||||
|
||||
inline const char* Path() const;
|
||||
inline const FileSystemId& FsId() const;
|
||||
|
||||
inline uint64 AllocFileId();
|
||||
|
||||
inline dev_t DevId() const;
|
||||
inline InodeIdMap* InoIdMap();
|
||||
|
||||
inline uint64 OpenOwner() const;
|
||||
inline uint32 OpenOwnerSequenceLock();
|
||||
inline void OpenOwnerSequenceUnlock(uint32 sequence);
|
||||
|
||||
inline bool NamedAttrs();
|
||||
inline void SetNamedAttrs(bool attrs);
|
||||
|
||||
inline const MountConfiguration& GetConfiguration();
|
||||
|
||||
inline mutex& CreateFileLock();
|
||||
private:
|
||||
FileSystem(const MountConfiguration& config);
|
||||
|
||||
mutex fCreateFileLock;
|
||||
|
||||
mutex fDelegationLock;
|
||||
DoublyLinkedList<Delegation> fDelegationList;
|
||||
AVLTreeMap<FileHandle, Delegation*> fHandleToDelegation;
|
||||
|
||||
DoublyLinkedList<OpenState> fOpenFiles;
|
||||
uint32 fOpenCount;
|
||||
mutex fOpenLock;
|
||||
|
||||
uint64 fOpenOwner;
|
||||
uint32 fOpenOwnerSequence;
|
||||
mutex fOpenOwnerLock;
|
||||
|
||||
uint32 fExpireType;
|
||||
uint32 fSupAttrs[2];
|
||||
bool fNamedAttrs;
|
||||
|
||||
FileSystemId fFsId;
|
||||
const char* fPath;
|
||||
|
||||
RootInode* fRoot;
|
||||
|
||||
RPC::Server* fServer;
|
||||
|
||||
vint64 fId;
|
||||
dev_t fDevId;
|
||||
|
||||
InodeIdMap fInoIdMap;
|
||||
|
||||
MountConfiguration fConfiguration;
|
||||
};
|
||||
|
||||
|
||||
inline RootInode*
|
||||
FileSystem::Root()
|
||||
{
|
||||
return fRoot;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
FileSystem::OpenFilesCount()
|
||||
{
|
||||
return fOpenCount;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
FileSystem::IsAttrSupported(Attribute attr) const
|
||||
{
|
||||
return sIsAttrSet(attr, fSupAttrs, 2);
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
FileSystem::ExpireType() const
|
||||
{
|
||||
return fExpireType;
|
||||
}
|
||||
|
||||
|
||||
inline RPC::Server*
|
||||
FileSystem::Server()
|
||||
{
|
||||
ASSERT(fServer != NULL);
|
||||
return fServer;
|
||||
}
|
||||
|
||||
|
||||
inline NFS4Server*
|
||||
FileSystem::NFSServer()
|
||||
{
|
||||
ASSERT(fServer->PrivateData() != NULL);
|
||||
return reinterpret_cast<NFS4Server*>(fServer->PrivateData());
|
||||
}
|
||||
|
||||
|
||||
inline const char*
|
||||
FileSystem::Path() const
|
||||
{
|
||||
ASSERT(fPath != NULL);
|
||||
return fPath;
|
||||
}
|
||||
|
||||
|
||||
inline const FileSystemId&
|
||||
FileSystem::FsId() const
|
||||
{
|
||||
return fFsId;
|
||||
}
|
||||
|
||||
|
||||
inline uint64
|
||||
FileSystem::AllocFileId()
|
||||
{
|
||||
return atomic_add64(&fId, 1);
|
||||
}
|
||||
|
||||
|
||||
inline dev_t
|
||||
FileSystem::DevId() const
|
||||
{
|
||||
return fDevId;
|
||||
}
|
||||
|
||||
|
||||
inline InodeIdMap*
|
||||
FileSystem::InoIdMap()
|
||||
{
|
||||
return &fInoIdMap;
|
||||
}
|
||||
|
||||
|
||||
inline uint64
|
||||
FileSystem::OpenOwner() const
|
||||
{
|
||||
return fOpenOwner;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
FileSystem::OpenOwnerSequenceLock()
|
||||
{
|
||||
mutex_lock(&fOpenOwnerLock);
|
||||
return fOpenOwnerSequence;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
FileSystem::OpenOwnerSequenceUnlock(uint32 sequence)
|
||||
{
|
||||
fOpenOwnerSequence = sequence;
|
||||
mutex_unlock(&fOpenOwnerLock);
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
FileSystem::NamedAttrs()
|
||||
{
|
||||
return fNamedAttrs;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
FileSystem::SetNamedAttrs(bool attrs)
|
||||
{
|
||||
fNamedAttrs = attrs;
|
||||
}
|
||||
|
||||
|
||||
inline const MountConfiguration&
|
||||
FileSystem::GetConfiguration()
|
||||
{
|
||||
return fConfiguration;
|
||||
}
|
||||
|
||||
|
||||
inline mutex&
|
||||
FileSystem::CreateFileLock()
|
||||
{
|
||||
return fCreateFileLock;
|
||||
}
|
||||
|
||||
|
||||
#endif // FILESYSTEM_H
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "IdMap.h"
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <team.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "idmapper/IdMapper.h"
|
||||
|
||||
|
||||
IdMap* gIdMapper = NULL;
|
||||
mutex gIdMapperLock;
|
||||
|
||||
|
||||
IdMap::IdMap()
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
fInitStatus = _Repair();
|
||||
}
|
||||
|
||||
|
||||
IdMap::~IdMap()
|
||||
{
|
||||
delete_port(fRequestPort);
|
||||
delete_port(fReplyPort);
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
uid_t
|
||||
IdMap::GetUserId(const char* owner)
|
||||
{
|
||||
ASSERT(owner != NULL);
|
||||
return _GetValue<uid_t>(owner, MsgNameToUID);
|
||||
}
|
||||
|
||||
|
||||
gid_t
|
||||
IdMap::GetGroupId(const char* ownerGroup)
|
||||
{
|
||||
ASSERT(ownerGroup != NULL);
|
||||
return _GetValue<gid_t>(ownerGroup, MsgNameToGID);
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
IdMap::GetOwner(uid_t user)
|
||||
{
|
||||
return reinterpret_cast<char*>(_GetBuffer(user, MsgUIDToName));
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
IdMap::GetOwnerGroup(gid_t group)
|
||||
{
|
||||
return reinterpret_cast<char*>(_GetBuffer(group, MsgGIDToName));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
IdMap::_GetValue(const char* buffer, int32 code)
|
||||
{
|
||||
ASSERT(buffer != NULL);
|
||||
|
||||
MutexLocker _(fLock);
|
||||
do {
|
||||
status_t result = write_port(fRequestPort, MsgNameToUID, buffer,
|
||||
strlen(buffer) + 1);
|
||||
if (result != B_OK) {
|
||||
if (_Repair() != B_OK)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 code;
|
||||
T value;
|
||||
result = read_port(fReplyPort, &code, &value, sizeof(T));
|
||||
if (result < B_OK) {
|
||||
if (_Repair() != B_OK)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code != MsgReply)
|
||||
return 0;
|
||||
|
||||
return value;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void*
|
||||
IdMap::_GetBuffer(T value, int32 code)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
do {
|
||||
status_t result = write_port(fRequestPort, code, &value, sizeof(value));
|
||||
if (result != B_OK) {
|
||||
if (_Repair() != B_OK)
|
||||
return NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t size = port_buffer_size(fReplyPort);
|
||||
if (size < B_OK) {
|
||||
if (_Repair() != B_OK)
|
||||
return NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 code;
|
||||
void* buffer = malloc(size);
|
||||
if (buffer == NULL)
|
||||
return NULL;
|
||||
MemoryDeleter bufferDeleter(buffer);
|
||||
|
||||
size = read_port(fReplyPort, &code, buffer, size);
|
||||
if (size < B_OK) {
|
||||
if (_Repair() != B_OK)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code != MsgReply)
|
||||
return NULL;
|
||||
|
||||
bufferDeleter.Detach();
|
||||
return buffer;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
IdMap::_Repair()
|
||||
{
|
||||
status_t result = B_OK;
|
||||
|
||||
fRequestPort = create_port(1, kRequestPortName);
|
||||
if (fRequestPort < B_OK)
|
||||
return fRequestPort;
|
||||
|
||||
fReplyPort = create_port(1, kReplyPortName);
|
||||
if (fReplyPort < B_OK) {
|
||||
delete_port(fRequestPort);
|
||||
return fReplyPort;
|
||||
}
|
||||
|
||||
char path[256];
|
||||
if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast<dev_t>(-1),
|
||||
false, path, sizeof(path)) != B_OK) {
|
||||
delete_port(fReplyPort);
|
||||
delete_port(fRequestPort);
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
strlcat(path, "/nfs4_idmapper_server", sizeof(path));
|
||||
|
||||
const char* args[] = { path, NULL };
|
||||
thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY,
|
||||
B_SYSTEM_TEAM, 0);
|
||||
if (thread < B_OK) {
|
||||
delete_port(fReplyPort);
|
||||
delete_port(fRequestPort);
|
||||
return thread;
|
||||
}
|
||||
|
||||
set_port_owner(fRequestPort, thread);
|
||||
set_port_owner(fReplyPort, thread);
|
||||
|
||||
result = resume_thread(thread);
|
||||
if (result != B_OK) {
|
||||
kill_thread(thread);
|
||||
delete_port(fReplyPort);
|
||||
delete_port(fRequestPort);
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
#ifndef IDMAP_H
|
||||
#define IDMAP_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
#include <port.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
class IdMap {
|
||||
public:
|
||||
IdMap();
|
||||
~IdMap();
|
||||
|
||||
uid_t GetUserId(const char* owner);
|
||||
gid_t GetGroupId(const char* ownerGroup);
|
||||
|
||||
char* GetOwner(uid_t user);
|
||||
char* GetOwnerGroup(gid_t group);
|
||||
|
||||
inline status_t InitStatus();
|
||||
|
||||
private:
|
||||
status_t _Repair();
|
||||
|
||||
template<typename T>
|
||||
void* _GetBuffer(T value, int32 code);
|
||||
|
||||
template<typename T>
|
||||
T _GetValue(const char* buffer, int32 code);
|
||||
|
||||
status_t fInitStatus;
|
||||
|
||||
mutex fLock;
|
||||
|
||||
port_id fRequestPort;
|
||||
port_id fReplyPort;
|
||||
};
|
||||
|
||||
|
||||
inline status_t
|
||||
IdMap::InitStatus()
|
||||
{
|
||||
return fInitStatus;
|
||||
}
|
||||
|
||||
|
||||
extern IdMap* gIdMapper;
|
||||
extern mutex gIdMapperLock;
|
||||
|
||||
|
||||
#endif // IDMAP_H
|
||||
|
||||
@@ -0,0 +1,981 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <fs_cache.h>
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
#include "IdMap.h"
|
||||
#include "Request.h"
|
||||
#include "RootInode.h"
|
||||
|
||||
|
||||
Inode::Inode()
|
||||
:
|
||||
fMetaCache(this),
|
||||
fCache(NULL),
|
||||
fAttrCache(NULL),
|
||||
fDelegation(NULL),
|
||||
fFileCache(NULL),
|
||||
fMaxFileSize(0),
|
||||
fOpenState(NULL),
|
||||
fWriteDirty(false),
|
||||
fAIOWait(create_sem(1, NULL)),
|
||||
fAIOCount(0)
|
||||
{
|
||||
rw_lock_init(&fDelegationLock, NULL);
|
||||
mutex_init(&fStateLock, NULL);
|
||||
mutex_init(&fFileCacheLock, NULL);
|
||||
rw_lock_init(&fWriteLock, NULL);
|
||||
mutex_init(&fAIOLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CreateInode(FileSystem* fs, const FileInfo& fi, Inode** _inode)
|
||||
{
|
||||
ASSERT(fs != NULL);
|
||||
ASSERT(_inode != NULL);
|
||||
|
||||
Inode* inode = NULL;
|
||||
if (fs->Root() == NULL)
|
||||
inode = new(std::nothrow) RootInode;
|
||||
else
|
||||
inode = new(std::nothrow) Inode;
|
||||
|
||||
if (inode == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
inode->fInfo = fi;
|
||||
inode->fFileSystem = fs;
|
||||
|
||||
uint64 size;
|
||||
do {
|
||||
RPC::Server* serv = fs->Server();
|
||||
Request request(serv, fs);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(inode->fInfo.fHandle);
|
||||
|
||||
Attribute attr[] = { FATTR4_TYPE, FATTR4_CHANGE, FATTR4_SIZE,
|
||||
FATTR4_FSID, FATTR4_FILEID };
|
||||
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
if (inode->HandleErrors(reply.NFS4Error(), serv))
|
||||
continue;
|
||||
|
||||
reply.PutFH();
|
||||
|
||||
AttrValue* values;
|
||||
uint32 count;
|
||||
result = reply.GetAttr(&values, &count);
|
||||
if (result != B_OK || count < 4)
|
||||
return result;
|
||||
|
||||
if (fi.fFileId == 0) {
|
||||
if (count < 5 || values[4].fAttribute != FATTR4_FILEID)
|
||||
inode->fInfo.fFileId = fs->AllocFileId();
|
||||
else
|
||||
inode->fInfo.fFileId = values[4].fData.fValue64;
|
||||
} else
|
||||
inode->fInfo.fFileId = fi.fFileId;
|
||||
|
||||
// FATTR4_TYPE is mandatory
|
||||
inode->fType = values[0].fData.fValue32;
|
||||
|
||||
if (inode->fType == NF4DIR)
|
||||
inode->fCache = new DirectoryCache(inode);
|
||||
inode->fAttrCache = new DirectoryCache(inode, true);
|
||||
|
||||
// FATTR4_CHANGE is mandatory
|
||||
inode->fChange = values[1].fData.fValue64;
|
||||
|
||||
// FATTR4_SIZE is mandatory
|
||||
size = values[2].fData.fValue64;
|
||||
|
||||
// FATTR4_FSID is mandatory
|
||||
FileSystemId* fsid
|
||||
= reinterpret_cast<FileSystemId*>(values[3].fData.fPointer);
|
||||
if (*fsid != fs->FsId()) {
|
||||
delete[] values;
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
delete[] values;
|
||||
|
||||
*_inode = inode;
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
if (inode->fType == NF4REG)
|
||||
inode->fFileCache = file_cache_create(fs->DevId(), inode->ID(), size);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
Inode::~Inode()
|
||||
{
|
||||
if (fDelegation != NULL)
|
||||
RecallDelegation();
|
||||
|
||||
if (fFileCache != NULL)
|
||||
file_cache_delete(fFileCache);
|
||||
|
||||
delete fCache;
|
||||
delete fAttrCache;
|
||||
|
||||
delete_sem(fAIOWait);
|
||||
mutex_destroy(&fAIOLock);
|
||||
mutex_destroy(&fStateLock);
|
||||
mutex_destroy(&fFileCacheLock);
|
||||
rw_lock_destroy(&fDelegationLock);
|
||||
rw_lock_destroy(&fWriteLock);
|
||||
|
||||
ASSERT(fAIOCount == 0);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::RevalidateFileCache()
|
||||
{
|
||||
if (fDelegation != NULL)
|
||||
return B_OK;
|
||||
|
||||
uint64 change;
|
||||
status_t result = GetChangeInfo(&change);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
MutexLocker _(fFileCacheLock);
|
||||
if (change == fChange)
|
||||
return B_OK;
|
||||
|
||||
struct stat st;
|
||||
result = Stat(&st);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
SyncAndCommit(true);
|
||||
file_cache_delete(fFileCache);
|
||||
|
||||
fFileCache = file_cache_create(fFileSystem->DevId(), ID(), st.st_size);
|
||||
|
||||
change = fChange;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::LookUp(const char* name, ino_t* id)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(id != NULL);
|
||||
|
||||
if (fType != NF4DIR)
|
||||
return B_NOT_A_DIRECTORY;
|
||||
|
||||
uint64 change;
|
||||
uint64 fileID;
|
||||
FileHandle handle;
|
||||
status_t result = NFS4Inode::LookUp(name, &change, &fileID, &handle);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
*id = FileIdToInoT(fileID);
|
||||
|
||||
result = ChildAdded(name, fileID, handle);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fCache->Lock();
|
||||
if (!fCache->Valid()) {
|
||||
fCache->Reset();
|
||||
fCache->SetChangeInfo(change);
|
||||
} else
|
||||
fCache->ValidateChangeInfo(change);
|
||||
|
||||
fCache->AddEntry(name, *id);
|
||||
fCache->Unlock();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Link(Inode* dir, const char* name)
|
||||
{
|
||||
ASSERT(dir != NULL);
|
||||
ASSERT(name != NULL);
|
||||
|
||||
ChangeInfo changeInfo;
|
||||
status_t result = NFS4Inode::Link(dir, name, &changeInfo);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fFileSystem->Root()->MakeInfoInvalid();
|
||||
|
||||
FileInfo fi = fInfo;
|
||||
fi.fParent = dir->fInfo.fHandle;
|
||||
result = fi.CreateName(fInfo.fPath, name);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fFileSystem->InoIdMap()->AddEntry(fi, fInfo.fFileId);
|
||||
|
||||
dir->fCache->Lock();
|
||||
if (dir->fCache->Valid()) {
|
||||
if (changeInfo.fAtomic
|
||||
&& dir->fCache->ChangeInfo() == changeInfo.fBefore) {
|
||||
dir->fCache->AddEntry(name, fInfo.fFileId, true);
|
||||
dir->fCache->SetChangeInfo(changeInfo.fAfter);
|
||||
} else
|
||||
dir->fCache->Trash();
|
||||
}
|
||||
dir->fCache->Unlock();
|
||||
|
||||
notify_entry_created(fFileSystem->DevId(), dir->ID(), name, ID());
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Remove(const char* name, FileType type, ino_t* id)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
|
||||
MemoryDeleter nameDeleter;
|
||||
if (type == NF4NAMEDATTR) {
|
||||
status_t result = LoadAttrDirHandle();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
name = AttrToFileName(name);
|
||||
if (name == NULL)
|
||||
return B_NO_MEMORY;
|
||||
nameDeleter.SetTo(const_cast<char*>(name));
|
||||
}
|
||||
|
||||
ChangeInfo changeInfo;
|
||||
uint64 fileID;
|
||||
status_t result = NFS4Inode::RemoveObject(name, type, &changeInfo, &fileID);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
DirectoryCache* cache = type != NF4NAMEDATTR ? fCache : fAttrCache;
|
||||
cache->Lock();
|
||||
if (cache->Valid()) {
|
||||
if (changeInfo.fAtomic
|
||||
&& fCache->ChangeInfo() == changeInfo.fBefore) {
|
||||
cache->RemoveEntry(name);
|
||||
cache->SetChangeInfo(changeInfo.fAfter);
|
||||
} else if (cache->ChangeInfo() != changeInfo.fBefore)
|
||||
cache->Trash();
|
||||
}
|
||||
cache->Unlock();
|
||||
|
||||
fFileSystem->Root()->MakeInfoInvalid();
|
||||
if (id != NULL)
|
||||
*id = FileIdToInoT(fileID);
|
||||
|
||||
if (type == NF4NAMEDATTR) {
|
||||
notify_attribute_changed(fFileSystem->DevId(), ID(), name,
|
||||
B_ATTR_REMOVED);
|
||||
} else {
|
||||
notify_entry_removed(fFileSystem->DevId(), ID(), name,
|
||||
FileIdToInoT(fileID));
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName,
|
||||
bool attribute, ino_t* id)
|
||||
{
|
||||
ASSERT(from != NULL);
|
||||
ASSERT(fromName != NULL);
|
||||
ASSERT(to != NULL);
|
||||
ASSERT(toName != NULL);
|
||||
|
||||
if (from->fFileSystem != to->fFileSystem)
|
||||
return B_DONT_DO_THAT;
|
||||
|
||||
MemoryDeleter fromNameDeleter;
|
||||
MemoryDeleter toNameDeleter;
|
||||
if (attribute) {
|
||||
status_t result = from->LoadAttrDirHandle();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
result = to->LoadAttrDirHandle();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fromName = from->AttrToFileName(fromName);
|
||||
toName = to->AttrToFileName(toName);
|
||||
|
||||
fromNameDeleter.SetTo(const_cast<char*>(fromName));
|
||||
toNameDeleter.SetTo(const_cast<char*>(toName));
|
||||
if (fromName == NULL || toName == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
ChangeInfo fromChange, toChange;
|
||||
uint64 fileID;
|
||||
status_t result = NFS4Inode::RenameNode(from, to, fromName, toName,
|
||||
&fromChange, &toChange, &fileID, attribute);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
from->fFileSystem->Root()->MakeInfoInvalid();
|
||||
|
||||
DirectoryCache* cache = attribute ? from->fAttrCache : from->fCache;
|
||||
cache->Lock();
|
||||
if (cache->Valid()) {
|
||||
if (fromChange.fAtomic
|
||||
&& cache->ChangeInfo() == fromChange.fBefore) {
|
||||
cache->RemoveEntry(fromName);
|
||||
cache->SetChangeInfo(fromChange.fAfter);
|
||||
} else if (cache->ChangeInfo() != fromChange.fBefore)
|
||||
cache->Trash();
|
||||
}
|
||||
cache->Unlock();
|
||||
|
||||
if (id != NULL)
|
||||
*id = FileIdToInoT(fileID);
|
||||
|
||||
cache = attribute ? to->fAttrCache : to->fCache;
|
||||
cache->Lock();
|
||||
if (cache->Valid()) {
|
||||
if (toChange.fAtomic
|
||||
&& cache->ChangeInfo() == toChange.fBefore) {
|
||||
cache->AddEntry(toName, fileID, true);
|
||||
cache->SetChangeInfo(toChange.fAfter);
|
||||
} else if (to->fCache->ChangeInfo() != toChange.fBefore)
|
||||
cache->Trash();
|
||||
}
|
||||
cache->Unlock();
|
||||
|
||||
if (attribute) {
|
||||
notify_attribute_changed(from->fFileSystem->DevId(), from->ID(),
|
||||
fromName, B_ATTR_REMOVED);
|
||||
notify_attribute_changed(to->fFileSystem->DevId(), to->ID(), toName,
|
||||
B_ATTR_CREATED);
|
||||
} else {
|
||||
notify_entry_moved(from->fFileSystem->DevId(), from->ID(), fromName,
|
||||
to->ID(), toName, FileIdToInoT(fileID));
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CreateLink(const char* name, const char* path, int mode, ino_t* id)
|
||||
{
|
||||
return CreateObject(name, path, mode, NF4LNK, id);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CreateObject(const char* name, const char* path, int mode, FileType type,
|
||||
ino_t* id)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(type != NF4LNK || path != NULL);
|
||||
|
||||
ChangeInfo changeInfo;
|
||||
uint64 fileID;
|
||||
FileHandle handle;
|
||||
|
||||
status_t result = NFS4Inode::CreateObject(name, path, mode, type, &changeInfo,
|
||||
&fileID, &handle);
|
||||
if (result != B_OK)
|
||||
return B_OK;
|
||||
|
||||
fFileSystem->Root()->MakeInfoInvalid();
|
||||
|
||||
result = ChildAdded(name, fileID, handle);
|
||||
if (result != B_OK)
|
||||
return B_OK;
|
||||
|
||||
fCache->Lock();
|
||||
if (fCache->Valid()) {
|
||||
if (changeInfo.fAtomic && fCache->ChangeInfo() == changeInfo.fBefore) {
|
||||
fCache->AddEntry(name, fileID, true);
|
||||
fCache->SetChangeInfo(changeInfo.fAfter);
|
||||
} else
|
||||
fCache->Trash();
|
||||
}
|
||||
fCache->Unlock();
|
||||
|
||||
notify_entry_created(fFileSystem->DevId(), ID(), name,
|
||||
FileIdToInoT(fileID));
|
||||
|
||||
*id = FileIdToInoT(fileID);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Access(int mode)
|
||||
{
|
||||
int acc = 0;
|
||||
|
||||
uint32 allowed;
|
||||
bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
|
||||
status_t result = fMetaCache.GetAccess(geteuid(), &allowed);
|
||||
if (result != B_OK || !cache) {
|
||||
result = NFS4Inode::Access(&allowed);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
fMetaCache.SetAccess(geteuid(), allowed);
|
||||
}
|
||||
|
||||
if ((allowed & ACCESS4_READ) != 0)
|
||||
acc |= R_OK;
|
||||
|
||||
if ((allowed & ACCESS4_LOOKUP) != 0)
|
||||
acc |= X_OK | R_OK;
|
||||
|
||||
if ((allowed & ACCESS4_EXECUTE) != 0)
|
||||
acc |= X_OK;
|
||||
|
||||
if ((allowed & ACCESS4_MODIFY) != 0)
|
||||
acc |= W_OK;
|
||||
|
||||
if ((mode & acc) != mode)
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Stat(struct stat* st, OpenAttrCookie* attr)
|
||||
{
|
||||
ASSERT(st != NULL);
|
||||
|
||||
if (attr != NULL)
|
||||
return GetStat(st, attr);
|
||||
|
||||
bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
|
||||
if (!cache)
|
||||
return GetStat(st, NULL);
|
||||
|
||||
status_t result = fMetaCache.GetStat(st);
|
||||
if (result != B_OK) {
|
||||
struct stat temp;
|
||||
result = GetStat(&temp);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
fMetaCache.SetStat(temp);
|
||||
fMetaCache.GetStat(st);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::GetStat(struct stat* st, OpenAttrCookie* attr)
|
||||
{
|
||||
ASSERT(st != NULL);
|
||||
|
||||
AttrValue* values;
|
||||
uint32 count;
|
||||
status_t result = NFS4Inode::GetStat(&values, &count, attr);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
// FATTR4_SIZE is mandatory
|
||||
if (count < 1 || values[0].fAttribute != FATTR4_SIZE) {
|
||||
delete[] values;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
st->st_size = values[0].fData.fValue64;
|
||||
|
||||
uint32 next = 1;
|
||||
st->st_mode = Type();
|
||||
if (count >= next && values[next].fAttribute == FATTR4_MODE) {
|
||||
st->st_mode |= values[next].fData.fValue32;
|
||||
next++;
|
||||
} else
|
||||
st->st_mode = 777;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_NUMLINKS) {
|
||||
st->st_nlink = values[next].fData.fValue32;
|
||||
next++;
|
||||
} else
|
||||
st->st_nlink = 1;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_OWNER) {
|
||||
char* owner = reinterpret_cast<char*>(values[next].fData.fPointer);
|
||||
if (owner != NULL && isdigit(owner[0]))
|
||||
st->st_uid = atoi(owner);
|
||||
else
|
||||
st->st_uid = gIdMapper->GetUserId(owner);
|
||||
next++;
|
||||
} else
|
||||
st->st_uid = 0;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_OWNER_GROUP) {
|
||||
char* group = reinterpret_cast<char*>(values[next].fData.fPointer);
|
||||
if (group != NULL && isdigit(group[0]))
|
||||
st->st_gid = atoi(group);
|
||||
else
|
||||
st->st_gid = gIdMapper->GetGroupId(group);
|
||||
next++;
|
||||
} else
|
||||
st->st_gid = 0;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_TIME_ACCESS) {
|
||||
memcpy(&st->st_atim, values[next].fData.fPointer,
|
||||
sizeof(timespec));
|
||||
next++;
|
||||
} else
|
||||
memset(&st->st_atim, 0, sizeof(timespec));
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_TIME_CREATE) {
|
||||
memcpy(&st->st_crtim, values[next].fData.fPointer,
|
||||
sizeof(timespec));
|
||||
next++;
|
||||
} else
|
||||
memset(&st->st_crtim, 0, sizeof(timespec));
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_TIME_METADATA) {
|
||||
memcpy(&st->st_ctim, values[next].fData.fPointer,
|
||||
sizeof(timespec));
|
||||
next++;
|
||||
} else
|
||||
memset(&st->st_ctim, 0, sizeof(timespec));
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_TIME_MODIFY) {
|
||||
memcpy(&st->st_mtim, values[next].fData.fPointer,
|
||||
sizeof(timespec));
|
||||
next++;
|
||||
} else
|
||||
memset(&st->st_mtim, 0, sizeof(timespec));
|
||||
delete[] values;
|
||||
|
||||
st->st_blksize = fFileSystem->Root()->IOSize();
|
||||
st->st_blocks = st->st_size / st->st_blksize;
|
||||
st->st_blocks += st->st_size % st->st_blksize == 0 ? 0 : 1;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie)
|
||||
{
|
||||
ASSERT(st != NULL);
|
||||
|
||||
status_t result;
|
||||
AttrValue attr[6];
|
||||
uint32 i = 0;
|
||||
|
||||
if ((mask & B_STAT_SIZE) != 0) {
|
||||
attr[i].fAttribute = FATTR4_SIZE;
|
||||
attr[i].fFreePointer = false;
|
||||
attr[i].fData.fValue64 = st->st_size;
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((mask & B_STAT_MODE) != 0) {
|
||||
attr[i].fAttribute = FATTR4_MODE;
|
||||
attr[i].fFreePointer = false;
|
||||
attr[i].fData.fValue32 = st->st_mode;
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((mask & B_STAT_UID) != 0) {
|
||||
attr[i].fAttribute = FATTR4_OWNER;
|
||||
attr[i].fFreePointer = true;
|
||||
attr[i].fData.fPointer = gIdMapper->GetOwner(st->st_uid);
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((mask & B_STAT_GID) != 0) {
|
||||
attr[i].fAttribute = FATTR4_OWNER_GROUP;
|
||||
attr[i].fFreePointer = true;
|
||||
attr[i].fData.fPointer = gIdMapper->GetOwnerGroup(st->st_gid);
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((mask & B_STAT_ACCESS_TIME) != 0) {
|
||||
attr[i].fAttribute = FATTR4_TIME_ACCESS_SET;
|
||||
attr[i].fFreePointer = true;
|
||||
attr[i].fData.fPointer = malloc(sizeof(st->st_atim));
|
||||
memcpy(attr[i].fData.fPointer, &st->st_atim, sizeof(st->st_atim));
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
|
||||
attr[i].fAttribute = FATTR4_TIME_MODIFY_SET;
|
||||
attr[i].fFreePointer = true;
|
||||
attr[i].fData.fPointer = malloc(sizeof(st->st_mtim));
|
||||
memcpy(attr[i].fData.fPointer, &st->st_mtim, sizeof(st->st_mtim));
|
||||
i++;
|
||||
}
|
||||
|
||||
if (cookie == NULL) {
|
||||
MutexLocker stateLocker(fStateLock);
|
||||
ASSERT(fOpenState != NULL);
|
||||
result = NFS4Inode::WriteStat(fOpenState, attr, i);
|
||||
stateLocker.Unlock();
|
||||
|
||||
fMetaCache.InvalidateStat();
|
||||
if ((mask & B_STAT_MODE) != 0 || (mask & B_STAT_UID) != 0
|
||||
|| (mask & B_STAT_GID) != 0) {
|
||||
fMetaCache.InvalidateAccess();
|
||||
}
|
||||
} else
|
||||
result = NFS4Inode::WriteStat(cookie->fOpenState, attr, i);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
Inode::CheckLockType(short ltype, uint32 mode)
|
||||
{
|
||||
switch (ltype) {
|
||||
case F_UNLCK:
|
||||
return B_OK;
|
||||
|
||||
case F_RDLCK:
|
||||
if ((mode & O_RDONLY) == 0 && (mode & O_RDWR) == 0)
|
||||
return EBADF;
|
||||
return B_OK;
|
||||
|
||||
case F_WRLCK:
|
||||
if ((mode & O_WRONLY) == 0 && (mode & O_RDWR) == 0)
|
||||
return EBADF;
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::TestLock(OpenFileCookie* cookie, struct flock* lock)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
ASSERT(lock != NULL);
|
||||
|
||||
if (lock->l_type == F_UNLCK)
|
||||
return B_OK;
|
||||
|
||||
status_t result = CheckLockType(lock->l_type, cookie->fMode);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
LockType ltype = sGetLockType(lock->l_type, false);
|
||||
uint64 position = lock->l_start;
|
||||
uint64 length = lock->l_len;
|
||||
bool conflict;
|
||||
|
||||
result = NFS4Inode::TestLock(cookie, <ype, &position, &length, conflict);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if (conflict) {
|
||||
lock->l_type = sLockTypeToHaiku(ltype);
|
||||
lock->l_start = static_cast<off_t>(position);
|
||||
if (length >= OFF_MAX)
|
||||
lock->l_len = OFF_MAX;
|
||||
else
|
||||
lock->l_len = static_cast<off_t>(length);
|
||||
} else
|
||||
lock->l_type = F_UNLCK;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::AcquireLock(OpenFileCookie* cookie, const struct flock* lock,
|
||||
bool wait)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
ASSERT(lock != NULL);
|
||||
|
||||
OpenState* state = cookie->fOpenState;
|
||||
|
||||
status_t result = CheckLockType(lock->l_type, cookie->fMode);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
thread_info info;
|
||||
get_thread_info(find_thread(NULL), &info);
|
||||
|
||||
MutexLocker locker(state->fOwnerLock);
|
||||
LockOwner* owner = state->GetLockOwner(info.team);
|
||||
if (owner == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
LockInfo* linfo = new LockInfo(owner);
|
||||
if (linfo == NULL)
|
||||
return B_NO_MEMORY;
|
||||
locker.Unlock();
|
||||
|
||||
linfo->fStart = lock->l_start;
|
||||
if (lock->l_len + lock->l_start == OFF_MAX)
|
||||
linfo->fLength = UINT64_MAX;
|
||||
else
|
||||
linfo->fLength = lock->l_len;
|
||||
linfo->fType = sGetLockType(lock->l_type, wait);
|
||||
|
||||
result = NFS4Inode::AcquireLock(cookie, linfo, wait);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
MutexLocker _(state->fLocksLock);
|
||||
state->AddLock(linfo);
|
||||
cookie->AddLock(linfo);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ReleaseLock(OpenFileCookie* cookie, const struct flock* lock)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
ASSERT(lock != NULL);
|
||||
|
||||
SyncAndCommit();
|
||||
|
||||
LockInfo* prev = NULL;
|
||||
|
||||
thread_info info;
|
||||
get_thread_info(find_thread(NULL), &info);
|
||||
uint32 owner = info.team;
|
||||
|
||||
OpenState* state = cookie->fOpenState;
|
||||
MutexLocker locker(state->fLocksLock);
|
||||
LockInfo* linfo = state->fLocks;
|
||||
while (linfo != NULL) {
|
||||
if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
|
||||
state->RemoveLock(linfo, prev);
|
||||
break;
|
||||
}
|
||||
|
||||
prev = linfo;
|
||||
linfo = linfo->fNext;
|
||||
}
|
||||
|
||||
prev = NULL;
|
||||
linfo = cookie->fLocks;
|
||||
while (linfo != NULL) {
|
||||
if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
|
||||
cookie->RemoveLock(linfo, prev);
|
||||
break;
|
||||
}
|
||||
|
||||
prev = linfo;
|
||||
linfo = linfo->fCookieNext;
|
||||
}
|
||||
locker.Unlock();
|
||||
|
||||
if (linfo == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
status_t result = NFS4Inode::ReleaseLock(cookie, linfo);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
state->DeleteLock(linfo);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ReleaseAllLocks(OpenFileCookie* cookie)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
|
||||
SyncAndCommit();
|
||||
|
||||
OpenState* state = cookie->fOpenState;
|
||||
MutexLocker _(state->fLocksLock);
|
||||
LockInfo* linfo = cookie->fLocks;
|
||||
while (linfo != NULL) {
|
||||
cookie->RemoveLock(linfo, NULL);
|
||||
|
||||
LockInfo* prev = NULL;
|
||||
LockInfo* stateLock = state->fLocks;
|
||||
while (stateLock != NULL) {
|
||||
if (*linfo == *stateLock) {
|
||||
state->RemoveLock(stateLock, prev);
|
||||
break;
|
||||
}
|
||||
|
||||
prev = stateLock;
|
||||
stateLock = stateLock->fNext;
|
||||
}
|
||||
|
||||
NFS4Inode::ReleaseLock(cookie, linfo);
|
||||
state->DeleteLock(linfo);
|
||||
|
||||
linfo = cookie->fLocks;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ChildAdded(const char* name, uint64 fileID,
|
||||
const FileHandle& fileHandle)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
|
||||
fFileSystem->Root()->MakeInfoInvalid();
|
||||
|
||||
FileInfo fi;
|
||||
fi.fFileId = fileID;
|
||||
fi.fHandle = fileHandle;
|
||||
fi.fParent = fInfo.fHandle;
|
||||
status_t result = fi.CreateName(fInfo.fPath, name);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
return fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID));
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Inode::Name() const
|
||||
{
|
||||
return fInfo.fName;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::SetDelegation(Delegation* delegation)
|
||||
{
|
||||
ASSERT(delegation != NULL);
|
||||
|
||||
WriteLocker _(fDelegationLock);
|
||||
|
||||
fMetaCache.InvalidateStat();
|
||||
struct stat st;
|
||||
Stat(&st);
|
||||
fMetaCache.LockValid();
|
||||
|
||||
fDelegation = delegation;
|
||||
fOpenState->AcquireReference();
|
||||
fOpenState->fDelegation = delegation;
|
||||
fFileSystem->AddDelegation(delegation);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::RecallDelegation(bool truncate)
|
||||
{
|
||||
WriteLocker _(fDelegationLock);
|
||||
if (fDelegation == NULL)
|
||||
return;
|
||||
ReturnDelegation(truncate);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::RecallReadDelegation()
|
||||
{
|
||||
WriteLocker _(fDelegationLock);
|
||||
if (fDelegation == NULL || fDelegation->Type() != OPEN_DELEGATE_READ)
|
||||
return;
|
||||
ReturnDelegation(false);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::ReturnDelegation(bool truncate)
|
||||
{
|
||||
ASSERT(fDelegation != NULL);
|
||||
|
||||
fDelegation->GiveUp(truncate);
|
||||
|
||||
fMetaCache.UnlockValid();
|
||||
fFileSystem->RemoveDelegation(fDelegation);
|
||||
|
||||
MutexLocker stateLocker(fStateLock);
|
||||
fOpenState->fDelegation = NULL;
|
||||
ReleaseOpenState();
|
||||
|
||||
delete fDelegation;
|
||||
fDelegation = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::ReleaseOpenState()
|
||||
{
|
||||
ASSERT(fOpenState != NULL);
|
||||
|
||||
if (fOpenState->ReleaseReference() == 1) {
|
||||
ASSERT(fAIOCount == 0);
|
||||
fOpenState = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::SyncAndCommit(bool force)
|
||||
{
|
||||
if (!force && fDelegation != NULL)
|
||||
return B_OK;
|
||||
|
||||
file_cache_sync(fFileCache);
|
||||
WaitAIOComplete();
|
||||
return Commit();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::BeginAIOOp()
|
||||
{
|
||||
MutexLocker _(fAIOLock);
|
||||
fAIOCount++;
|
||||
if (fAIOCount == 1)
|
||||
acquire_sem(fAIOWait);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::EndAIOOp()
|
||||
{
|
||||
MutexLocker _(fAIOLock);
|
||||
ASSERT(fAIOCount > 0);
|
||||
fAIOCount--;
|
||||
if (fAIOCount == 0)
|
||||
release_sem(fAIOWait);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef INODE_H
|
||||
#define INODE_H
|
||||
|
||||
|
||||
#include "DirectoryCache.h"
|
||||
#include "MetadataCache.h"
|
||||
#include "NFS4Inode.h"
|
||||
#include "OpenState.h"
|
||||
|
||||
|
||||
class Delegation;
|
||||
|
||||
class Inode : public NFS4Inode {
|
||||
public:
|
||||
static status_t CreateInode(FileSystem* fs, const FileInfo& fi,
|
||||
Inode** inode);
|
||||
virtual ~Inode();
|
||||
|
||||
inline ino_t ID() const;
|
||||
inline mode_t Type() const;
|
||||
virtual const char* Name() const;
|
||||
inline FileSystem* GetFileSystem() const;
|
||||
|
||||
inline void SetOpenState(OpenState* state);
|
||||
|
||||
inline void* FileCache();
|
||||
status_t RevalidateFileCache();
|
||||
|
||||
inline uint64 MaxFileSize();
|
||||
|
||||
inline uint64 Change();
|
||||
inline bool Dirty();
|
||||
|
||||
inline OpenState* GetOpenState();
|
||||
|
||||
void SetDelegation(Delegation* delegation);
|
||||
void RecallDelegation(bool truncate = false);
|
||||
void RecallReadDelegation();
|
||||
|
||||
status_t LookUp(const char* name, ino_t* id);
|
||||
|
||||
status_t Access(int mode);
|
||||
|
||||
status_t Commit();
|
||||
status_t SyncAndCommit(bool force = false);
|
||||
|
||||
status_t CreateObject(const char* name, const char* path,
|
||||
int mode, FileType type, ino_t* id);
|
||||
|
||||
status_t CreateLink(const char* name, const char* path,
|
||||
int mode, ino_t* id);
|
||||
|
||||
status_t Link(Inode* dir, const char* name);
|
||||
status_t Remove(const char* name, FileType type,
|
||||
ino_t* id = NULL);
|
||||
static status_t Rename(Inode* from, Inode* to,
|
||||
const char* fromName, const char* toName,
|
||||
bool attribute = false, ino_t* id = NULL);
|
||||
|
||||
status_t Stat(struct stat* st,
|
||||
OpenAttrCookie* attr = NULL);
|
||||
status_t WriteStat(const struct stat* st, uint32 mask,
|
||||
OpenAttrCookie* attr = NULL);
|
||||
|
||||
status_t Create(const char* name, int mode, int perms,
|
||||
OpenFileCookie* cookie,
|
||||
OpenDelegationData* data, ino_t* id);
|
||||
status_t Open(int mode, OpenFileCookie* cookie);
|
||||
status_t Close(OpenFileCookie* cookie);
|
||||
|
||||
status_t OpenAttr(const char* name, int mode,
|
||||
OpenAttrCookie* cookie, bool create,
|
||||
int32 type = 0);
|
||||
status_t CloseAttr(OpenAttrCookie* cookie);
|
||||
|
||||
status_t Read(OpenFileCookie* cookie, off_t pos,
|
||||
void* buffer, size_t* length);
|
||||
status_t Write(OpenFileCookie* cookie, off_t pos,
|
||||
const void* buffer, size_t* _length);
|
||||
|
||||
status_t ReadDirect(OpenStateCookie* cookie, off_t pos,
|
||||
void* buffer, size_t* length, bool* eof);
|
||||
status_t WriteDirect(OpenStateCookie* cookie, off_t pos,
|
||||
const void* buffer, size_t* _length);
|
||||
|
||||
status_t CreateDir(const char* name, int mode,
|
||||
ino_t* id);
|
||||
status_t OpenDir(OpenDirCookie* cookie);
|
||||
status_t ReadDir(void* buffer, uint32 size,
|
||||
uint32* count, OpenDirCookie* cookie);
|
||||
|
||||
status_t OpenAttrDir(OpenDirCookie* cookie);
|
||||
|
||||
status_t TestLock(OpenFileCookie* cookie,
|
||||
struct flock* lock);
|
||||
status_t AcquireLock(OpenFileCookie* cookie,
|
||||
const struct flock* lock, bool wait);
|
||||
status_t ReleaseLock(OpenFileCookie* cookie,
|
||||
const struct flock* lock);
|
||||
status_t ReleaseAllLocks(OpenFileCookie* cookie);
|
||||
|
||||
status_t GetDirSnapshot(DirectoryCacheSnapshot**
|
||||
_snapshot, OpenDirCookie* cookie,
|
||||
uint64* _change, bool attribute);
|
||||
|
||||
status_t LoadAttrDirHandle();
|
||||
|
||||
static inline ino_t FileIdToInoT(uint64 fileid);
|
||||
|
||||
void BeginAIOOp();
|
||||
void EndAIOOp();
|
||||
inline void WaitAIOComplete();
|
||||
protected:
|
||||
Inode();
|
||||
|
||||
void ReleaseOpenState();
|
||||
|
||||
status_t CreateState(const char* name, int mode,
|
||||
int perms, OpenState* state,
|
||||
OpenDelegationData* data);
|
||||
|
||||
void ReturnDelegation(bool truncate);
|
||||
|
||||
status_t ReadDirUp(struct dirent* de, uint32 pos,
|
||||
uint32 size);
|
||||
status_t FillDirEntry(struct dirent* de, ino_t id,
|
||||
const char* name, uint32 pos, uint32 size);
|
||||
|
||||
status_t ChildAdded(const char* name, uint64 fileID,
|
||||
const FileHandle& fileHandle);
|
||||
|
||||
status_t GetStat(struct stat* st,
|
||||
OpenAttrCookie* attr = NULL);
|
||||
|
||||
char* AttrToFileName(const char* path);
|
||||
|
||||
static inline status_t CheckLockType(short ltype, uint32 mode);
|
||||
|
||||
private:
|
||||
uint32 fType;
|
||||
|
||||
MetadataCache fMetaCache;
|
||||
DirectoryCache* fCache;
|
||||
DirectoryCache* fAttrCache;
|
||||
|
||||
rw_lock fDelegationLock;
|
||||
Delegation* fDelegation;
|
||||
|
||||
uint64 fChange;
|
||||
void* fFileCache;
|
||||
mutex fFileCacheLock;
|
||||
uint64 fMaxFileSize;
|
||||
|
||||
OpenState* fOpenState;
|
||||
mutex fStateLock;
|
||||
|
||||
rw_lock fWriteLock;
|
||||
bool fWriteDirty;
|
||||
|
||||
sem_id fAIOWait;
|
||||
uint32 fAIOCount;
|
||||
mutex fAIOLock;
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
Inode::WaitAIOComplete()
|
||||
{
|
||||
acquire_sem(fAIOWait);
|
||||
release_sem(fAIOWait);
|
||||
}
|
||||
|
||||
|
||||
inline ino_t
|
||||
Inode::FileIdToInoT(uint64 fileid)
|
||||
{
|
||||
if (sizeof(ino_t) >= sizeof(uint64))
|
||||
return fileid;
|
||||
else
|
||||
return (ino_t)fileid ^ (fileid >>
|
||||
(sizeof(uint64) - sizeof(ino_t)) * 8);
|
||||
}
|
||||
|
||||
|
||||
inline ino_t
|
||||
Inode::ID() const
|
||||
{
|
||||
return FileIdToInoT(fInfo.fFileId);
|
||||
}
|
||||
|
||||
|
||||
inline mode_t
|
||||
Inode::Type() const
|
||||
{
|
||||
return sNFSFileTypeToHaiku[fType];
|
||||
}
|
||||
|
||||
|
||||
inline FileSystem*
|
||||
Inode::GetFileSystem() const
|
||||
{
|
||||
ASSERT(fFileSystem != NULL);
|
||||
return fFileSystem;
|
||||
}
|
||||
|
||||
|
||||
inline void*
|
||||
Inode::FileCache()
|
||||
{
|
||||
return fFileCache;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
Inode::SetOpenState(OpenState* state)
|
||||
{
|
||||
ASSERT(state != NULL);
|
||||
fOpenState = state;
|
||||
}
|
||||
|
||||
|
||||
inline uint64
|
||||
Inode::MaxFileSize()
|
||||
{
|
||||
return fMaxFileSize;
|
||||
}
|
||||
|
||||
|
||||
inline uint64
|
||||
Inode::Change()
|
||||
{
|
||||
return fChange;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
Inode::Dirty()
|
||||
{
|
||||
return fWriteDirty;
|
||||
}
|
||||
|
||||
|
||||
inline OpenState*
|
||||
Inode::GetOpenState()
|
||||
{
|
||||
return fOpenState;
|
||||
}
|
||||
|
||||
|
||||
#endif // INODE_H
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "IdMap.h"
|
||||
#include "Request.h"
|
||||
#include "RootInode.h"
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CreateDir(const char* name, int mode, ino_t* id)
|
||||
{
|
||||
return CreateObject(name, NULL, mode, NF4DIR, id);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::OpenDir(OpenDirCookie* cookie)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
|
||||
if (fType != NF4DIR)
|
||||
return B_NOT_A_DIRECTORY;
|
||||
|
||||
status_t result = Access(R_OK);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
cookie->fFileSystem = fFileSystem;
|
||||
cookie->fSpecial = 0;
|
||||
cookie->fSnapshot = NULL;
|
||||
cookie->fCurrent = NULL;
|
||||
cookie->fEOF = false;
|
||||
cookie->fAttrDir = false;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::OpenAttrDir(OpenDirCookie* cookie)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
|
||||
cookie->fFileSystem = fFileSystem;
|
||||
cookie->fSpecial = 0;
|
||||
cookie->fSnapshot = NULL;
|
||||
cookie->fCurrent = NULL;
|
||||
cookie->fEOF = false;
|
||||
cookie->fAttrDir = true;
|
||||
|
||||
return LoadAttrDirHandle();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::LoadAttrDirHandle()
|
||||
{
|
||||
if (fInfo.fAttrDir.fSize != 0)
|
||||
return B_OK;
|
||||
|
||||
FileHandle handle;
|
||||
status_t result;
|
||||
|
||||
if (fFileSystem->NamedAttrs()) {
|
||||
result = NFS4Inode::OpenAttrDir(&handle);
|
||||
if (result == B_OK) {
|
||||
fInfo.fAttrDir = handle;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (result != B_UNSUPPORTED)
|
||||
return result;
|
||||
|
||||
fFileSystem->SetNamedAttrs(false);
|
||||
}
|
||||
|
||||
if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs)
|
||||
return B_UNSUPPORTED;
|
||||
|
||||
char* attrDir
|
||||
= reinterpret_cast<char*>(malloc(strlen(Name()) + 32));
|
||||
if (attrDir == NULL)
|
||||
return B_NO_MEMORY;
|
||||
strcpy(attrDir, ".");
|
||||
strcat(attrDir, Name());
|
||||
strcat(attrDir, "-haiku-attrs");
|
||||
|
||||
result = NFS4Inode::LookUp(attrDir, NULL, NULL, &handle, true);
|
||||
if (result == B_ENTRY_NOT_FOUND) {
|
||||
ChangeInfo change;
|
||||
struct stat st;
|
||||
Stat(&st);
|
||||
st.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
|
||||
result = NFS4Inode::CreateObject(attrDir, NULL, st.st_mode, NF4DIR,
|
||||
&change, NULL, &handle, true);
|
||||
}
|
||||
|
||||
free(attrDir);
|
||||
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fInfo.fAttrDir = handle;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
|
||||
uint32 size)
|
||||
{
|
||||
ASSERT(de != NULL);
|
||||
ASSERT(name != NULL);
|
||||
|
||||
uint32 nameSize = strlen(name) + 1;
|
||||
const uint32 entSize = sizeof(struct dirent);
|
||||
|
||||
if (pos + entSize + nameSize > size)
|
||||
return B_BUFFER_OVERFLOW;
|
||||
|
||||
de->d_dev = fFileSystem->DevId();
|
||||
de->d_ino = id;
|
||||
de->d_reclen = entSize + nameSize;
|
||||
if (de->d_reclen % 8 != 0)
|
||||
de->d_reclen += 8 - de->d_reclen % 8;
|
||||
|
||||
strcpy(de->d_name, name);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
|
||||
{
|
||||
ASSERT(de != NULL);
|
||||
|
||||
do {
|
||||
RPC::Server* serv = fFileSystem->Server();
|
||||
Request request(serv, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
req.LookUpUp();
|
||||
req.GetFH();
|
||||
|
||||
if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
|
||||
Attribute attr[] = { FATTR4_FILEID };
|
||||
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
|
||||
}
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), serv))
|
||||
continue;
|
||||
|
||||
reply.PutFH();
|
||||
result = reply.LookUpUp();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
FileHandle fh;
|
||||
reply.GetFH(&fh);
|
||||
|
||||
uint64 fileId;
|
||||
if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
|
||||
AttrValue* values;
|
||||
uint32 count;
|
||||
reply.GetAttr(&values, &count);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fileId = values[0].fData.fValue64;
|
||||
delete[] values;
|
||||
} else
|
||||
fileId = fFileSystem->AllocFileId();
|
||||
|
||||
return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
FileToAttrName(const char* path)
|
||||
{
|
||||
ASSERT(path != NULL);
|
||||
|
||||
char* name = strdup(path);
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
char* current = strpbrk(name, "#$");
|
||||
while (current != NULL) {
|
||||
switch (*current) {
|
||||
case '#':
|
||||
*current = '/';
|
||||
break;
|
||||
case '$':
|
||||
*current = ':';
|
||||
break;
|
||||
}
|
||||
current = strpbrk(name, "#$");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
|
||||
OpenDirCookie* cookie, uint64* _change, bool attribute)
|
||||
{
|
||||
ASSERT(_snapshot != NULL);
|
||||
|
||||
DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot;
|
||||
if (snapshot == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint64 change = 0;
|
||||
uint64 dirCookie = 0;
|
||||
uint64 dirCookieVerf = 0;
|
||||
bool eof = false;
|
||||
|
||||
while (!eof) {
|
||||
uint32 count;
|
||||
DirEntry* dirents;
|
||||
|
||||
status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change,
|
||||
&dirCookie, &dirCookieVerf, attribute);
|
||||
if (result != B_OK) {
|
||||
delete snapshot;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 i;
|
||||
for (i = 0; i < count; i++) {
|
||||
|
||||
// FATTR4_FSID is mandatory
|
||||
void* data = dirents[i].fAttrs[0].fData.fPointer;
|
||||
FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data);
|
||||
if (*fsid != fFileSystem->FsId())
|
||||
continue;
|
||||
|
||||
if (strstr(dirents[i].fName, "-haiku-attrs") != NULL)
|
||||
continue;
|
||||
|
||||
ino_t id;
|
||||
if (!attribute) {
|
||||
if (dirents[i].fAttrCount == 2)
|
||||
id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
|
||||
else
|
||||
id = FileIdToInoT(fFileSystem->AllocFileId());
|
||||
} else
|
||||
id = 0;
|
||||
|
||||
const char* name = dirents[i].fName;
|
||||
if (attribute)
|
||||
name = FileToAttrName(name);
|
||||
if (name == NULL) {
|
||||
delete snapshot;
|
||||
delete[] dirents;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
NameCacheEntry* entry = new NameCacheEntry(name, id);
|
||||
if (attribute)
|
||||
free(const_cast<char*>(name));
|
||||
|
||||
if (entry == NULL || entry->fName == NULL) {
|
||||
if (entry != NULL)
|
||||
delete entry;
|
||||
delete snapshot;
|
||||
delete[] dirents;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
snapshot->fEntries.Add(entry);
|
||||
}
|
||||
|
||||
delete[] dirents;
|
||||
}
|
||||
|
||||
*_snapshot = snapshot;
|
||||
*_change = change;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
||||
OpenDirCookie* cookie)
|
||||
{
|
||||
ASSERT(_buffer != NULL);
|
||||
ASSERT(_count != NULL);
|
||||
ASSERT(cookie != NULL);
|
||||
|
||||
if (cookie->fEOF) {
|
||||
*_count = 0;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t result;
|
||||
DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache;
|
||||
if (cookie->fSnapshot == NULL) {
|
||||
cache->Lock();
|
||||
result = cache->Revalidate();
|
||||
if (result != B_OK) {
|
||||
cache->Unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
DirectoryCacheSnapshot* snapshot;
|
||||
result = cache->GetSnapshot(&snapshot);
|
||||
if (result != B_OK) {
|
||||
cache->Unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot);
|
||||
cache->Unlock();
|
||||
|
||||
if (cookie->fSnapshot == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
char* buffer = reinterpret_cast<char*>(_buffer);
|
||||
uint32 pos = 0;
|
||||
uint32 i = 0;
|
||||
bool overflow = false;
|
||||
|
||||
if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) {
|
||||
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
|
||||
|
||||
status_t result;
|
||||
result = FillDirEntry(de, fInfo.fFileId, ".", pos, size);
|
||||
|
||||
if (result == B_BUFFER_OVERFLOW)
|
||||
overflow = true;
|
||||
else if (result == B_OK) {
|
||||
pos += de->d_reclen;
|
||||
i++;
|
||||
cookie->fSpecial++;
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
|
||||
if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) {
|
||||
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
|
||||
|
||||
status_t result;
|
||||
result = ReadDirUp(de, pos, size);
|
||||
if (result == B_ENTRY_NOT_FOUND) {
|
||||
result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos,
|
||||
size);
|
||||
}
|
||||
|
||||
if (result == B_BUFFER_OVERFLOW)
|
||||
overflow = true;
|
||||
else if (result == B_OK) {
|
||||
pos += de->d_reclen;
|
||||
i++;
|
||||
cookie->fSpecial++;
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
|
||||
MutexLocker _(cookie->fSnapshot->fLock);
|
||||
for (; !overflow && i < *_count; i++) {
|
||||
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
|
||||
NameCacheEntry* temp = cookie->fCurrent;
|
||||
|
||||
if (cookie->fCurrent == NULL)
|
||||
cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
|
||||
else {
|
||||
cookie->fCurrent
|
||||
= cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
|
||||
}
|
||||
|
||||
if (cookie->fCurrent == NULL) {
|
||||
cookie->fEOF = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
|
||||
pos, size) == B_BUFFER_OVERFLOW) {
|
||||
cookie->fCurrent = temp;
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += de->d_reclen;
|
||||
}
|
||||
|
||||
if (i == 0 && overflow)
|
||||
return B_BUFFER_OVERFLOW;
|
||||
|
||||
*_count = i;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef INODEIDMAP_H
|
||||
#define INODEIDMAP_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <util/AVLTreeMap.h>
|
||||
|
||||
#include "FileInfo.h"
|
||||
|
||||
struct InodeIdMapEntry {
|
||||
FileInfo fFileInfo;
|
||||
bool fRemoved;
|
||||
};
|
||||
|
||||
class InodeIdMap {
|
||||
public:
|
||||
inline InodeIdMap();
|
||||
inline ~InodeIdMap();
|
||||
|
||||
inline status_t AddEntry(const FileInfo& fi,
|
||||
ino_t id, bool weak = false);
|
||||
inline status_t MarkRemoved(ino_t id);
|
||||
inline status_t RemoveEntry(ino_t id);
|
||||
inline status_t GetFileInfo(FileInfo* fi, ino_t id);
|
||||
|
||||
protected:
|
||||
inline bool _IsEntryRemoved(ino_t id);
|
||||
|
||||
private:
|
||||
AVLTreeMap<ino_t, InodeIdMapEntry> fMap;
|
||||
mutex fLock;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline
|
||||
InodeIdMap::InodeIdMap()
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
InodeIdMap::~InodeIdMap()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
InodeIdMap::AddEntry(const FileInfo& fi, ino_t id, bool weak)
|
||||
{
|
||||
InodeIdMapEntry entry;
|
||||
|
||||
MutexLocker _(fLock);
|
||||
if (!weak || _IsEntryRemoved(id))
|
||||
fMap.Remove(id);
|
||||
|
||||
entry.fFileInfo = fi;
|
||||
entry.fRemoved = false;
|
||||
|
||||
return fMap.Insert(id, entry);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
InodeIdMap::MarkRemoved(ino_t id)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
|
||||
if (!it.HasCurrent())
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
it.CurrentValuePointer()->fRemoved = true;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
InodeIdMap::RemoveEntry(ino_t id)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
if (_IsEntryRemoved(id))
|
||||
return fMap.Remove(id);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
InodeIdMap::GetFileInfo(FileInfo* fi, ino_t id)
|
||||
{
|
||||
ASSERT(fi != NULL);
|
||||
|
||||
MutexLocker _(fLock);
|
||||
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
|
||||
if (!it.HasCurrent())
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
*fi = it.Current().fFileInfo;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// Caller must hold fLock
|
||||
inline bool
|
||||
InodeIdMap::_IsEntryRemoved(ino_t id)
|
||||
{
|
||||
AVLTreeMap<ino_t, InodeIdMapEntry>::Iterator it = fMap.Find(id);
|
||||
if (!it.HasCurrent())
|
||||
return true;
|
||||
|
||||
return it.Current().fRemoved;
|
||||
}
|
||||
|
||||
|
||||
#endif // INODEIDMAP_H
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <fs_cache.h>
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
#include "IdMap.h"
|
||||
#include "Request.h"
|
||||
#include "RootInode.h"
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CreateState(const char* name, int mode, int perms, OpenState* state,
|
||||
OpenDelegationData* delegationData) {
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(state != NULL);
|
||||
ASSERT(delegationData != NULL);
|
||||
|
||||
uint64 fileID;
|
||||
FileHandle handle;
|
||||
ChangeInfo changeInfo;
|
||||
|
||||
status_t result = CreateFile(name, mode, perms, state, &changeInfo,
|
||||
&fileID, &handle, delegationData);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
FileInfo fi;
|
||||
fi.fFileId = fileID;
|
||||
fi.fHandle = handle;
|
||||
fi.fParent = fInfo.fHandle;
|
||||
fi.CreateName(fInfo.fPath, name);
|
||||
|
||||
fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID));
|
||||
|
||||
fCache->Lock();
|
||||
if (fCache->Valid()) {
|
||||
if (changeInfo.fAtomic
|
||||
&& fCache->ChangeInfo() == changeInfo.fBefore) {
|
||||
fCache->AddEntry(name, fileID, true);
|
||||
fCache->SetChangeInfo(changeInfo.fAfter);
|
||||
} else
|
||||
fCache->Trash();
|
||||
}
|
||||
fCache->Unlock();
|
||||
|
||||
state->fFileSystem = fFileSystem;
|
||||
state->fInfo = fi;
|
||||
state->fMode = mode & O_RWMASK;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
|
||||
OpenDelegationData* data, ino_t* id)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(cookie != NULL);
|
||||
ASSERT(data != NULL);
|
||||
|
||||
cookie->fMode = mode;
|
||||
cookie->fLocks = NULL;
|
||||
|
||||
OpenState* state = new OpenState;
|
||||
status_t result = CreateState(name, mode, perms, state, data);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
cookie->fOpenState = state;
|
||||
cookie->fFileSystem = fFileSystem;
|
||||
|
||||
*id = FileIdToInoT(state->fInfo.fFileId);
|
||||
|
||||
fFileSystem->AddOpenFile(state);
|
||||
fFileSystem->Root()->MakeInfoInvalid();
|
||||
|
||||
notify_entry_created(fFileSystem->DevId(), ID(), name, *id);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Open(int mode, OpenFileCookie* cookie)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
|
||||
MutexLocker locker(fStateLock);
|
||||
|
||||
OpenDelegationData data;
|
||||
data.fType = OPEN_DELEGATE_NONE;
|
||||
if (fOpenState == NULL) {
|
||||
RevalidateFileCache();
|
||||
|
||||
OpenState* state = new OpenState;
|
||||
if (state == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
state->fInfo = fInfo;
|
||||
state->fFileSystem = fFileSystem;
|
||||
state->fMode = mode & O_RWMASK;
|
||||
status_t result = OpenFile(state, mode, &data);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fFileSystem->AddOpenFile(state);
|
||||
fOpenState = state;
|
||||
cookie->fOpenState = state;
|
||||
locker.Unlock();
|
||||
} else {
|
||||
fOpenState->AcquireReference();
|
||||
cookie->fOpenState = fOpenState;
|
||||
locker.Unlock();
|
||||
|
||||
int newMode = mode & O_RWMASK;
|
||||
int oldMode = fOpenState->fMode & O_RWMASK;
|
||||
if (oldMode != newMode && oldMode != O_RDWR) {
|
||||
if (oldMode == O_RDONLY)
|
||||
RecallReadDelegation();
|
||||
|
||||
status_t result = OpenFile(fOpenState, O_RDWR, &data);
|
||||
if (result != B_OK) {
|
||||
locker.Lock();
|
||||
ReleaseOpenState();
|
||||
return result;
|
||||
}
|
||||
fOpenState->fMode = O_RDWR;
|
||||
} else {
|
||||
int newMode = mode & O_RWMASK;
|
||||
uint32 allowed = 0;
|
||||
if (newMode == O_RDWR || newMode == O_RDONLY)
|
||||
allowed |= R_OK;
|
||||
if (newMode == O_RDWR || newMode == O_WRONLY)
|
||||
allowed |= W_OK;
|
||||
|
||||
status_t result = Access(allowed);
|
||||
if (result != B_OK) {
|
||||
locker.Lock();
|
||||
ReleaseOpenState();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((mode & O_TRUNC) == O_TRUNC) {
|
||||
struct stat st;
|
||||
st.st_size = 0;
|
||||
WriteStat(&st, B_STAT_SIZE);
|
||||
file_cache_set_size(fFileCache, 0);
|
||||
}
|
||||
|
||||
cookie->fFileSystem = fFileSystem;
|
||||
cookie->fMode = mode;
|
||||
cookie->fLocks = NULL;
|
||||
|
||||
if (data.fType != OPEN_DELEGATE_NONE) {
|
||||
Delegation* delegation
|
||||
= new(std::nothrow) Delegation(data, this, fOpenState->fClientID);
|
||||
if (delegation != NULL) {
|
||||
delegation->fInfo = fOpenState->fInfo;
|
||||
delegation->fFileSystem = fFileSystem;
|
||||
SetDelegation(delegation);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Close(OpenFileCookie* cookie)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
ASSERT(fOpenState == cookie->fOpenState);
|
||||
|
||||
SyncAndCommit();
|
||||
|
||||
MutexLocker _(fStateLock);
|
||||
ReleaseOpenState();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
Inode::AttrToFileName(const char* path)
|
||||
{
|
||||
ASSERT(path != NULL);
|
||||
|
||||
char* name = strdup(path);
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
char* current = strpbrk(name, "/:");
|
||||
while (current != NULL) {
|
||||
switch (*current) {
|
||||
case '/':
|
||||
*current = '#';
|
||||
break;
|
||||
case ':':
|
||||
*current = '$';
|
||||
break;
|
||||
}
|
||||
current = strpbrk(name, "/:");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::OpenAttr(const char* _name, int mode, OpenAttrCookie* cookie,
|
||||
bool create, int32 type)
|
||||
{
|
||||
ASSERT(_name != NULL);
|
||||
ASSERT(cookie != NULL);
|
||||
|
||||
(void)type;
|
||||
|
||||
status_t result = LoadAttrDirHandle();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
char* name = AttrToFileName(_name);
|
||||
if (name == NULL)
|
||||
return B_NO_MEMORY;
|
||||
MemoryDeleter nameDeleter(name);
|
||||
|
||||
OpenDelegationData data;
|
||||
data.fType = OPEN_DELEGATE_NONE;
|
||||
|
||||
OpenState* state = new OpenState;
|
||||
if (state == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
state->fInfo.fName = strdup(name);
|
||||
state->fInfo.fParent = fInfo.fAttrDir;
|
||||
state->fFileSystem = fFileSystem;
|
||||
result = NFS4Inode::OpenAttr(state, name, mode, &data, create);
|
||||
if (result != B_OK) {
|
||||
delete state;
|
||||
return result;
|
||||
}
|
||||
|
||||
fFileSystem->AddOpenFile(state);
|
||||
|
||||
cookie->fOpenState = state;
|
||||
cookie->fFileSystem = fFileSystem;
|
||||
cookie->fMode = mode;
|
||||
|
||||
if (data.fType != OPEN_DELEGATE_NONE) {
|
||||
Delegation* delegation
|
||||
= new(std::nothrow) Delegation(data, this, state->fClientID, true);
|
||||
if (delegation != NULL) {
|
||||
delegation->fInfo = state->fInfo;
|
||||
delegation->fFileSystem = fFileSystem;
|
||||
state->fDelegation = delegation;
|
||||
fFileSystem->AddDelegation(delegation);
|
||||
}
|
||||
}
|
||||
|
||||
if (create || (mode & O_TRUNC) == O_TRUNC) {
|
||||
struct stat st;
|
||||
st.st_size = 0;
|
||||
WriteStat(&st, B_STAT_SIZE, cookie);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CloseAttr(OpenAttrCookie* cookie)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
|
||||
if (cookie->fOpenState->fDelegation != NULL) {
|
||||
cookie->fOpenState->fDelegation->GiveUp();
|
||||
fFileSystem->RemoveDelegation(cookie->fOpenState->fDelegation);
|
||||
}
|
||||
|
||||
delete cookie->fOpenState->fDelegation;
|
||||
delete cookie->fOpenState;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer,
|
||||
size_t* _length, bool* eof)
|
||||
{
|
||||
ASSERT(cookie != NULL || fOpenState != NULL);
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(_length != NULL);
|
||||
ASSERT(eof != NULL);
|
||||
|
||||
*eof = false;
|
||||
uint32 size = 0;
|
||||
|
||||
uint32 ioSize = fFileSystem->Root()->IOSize();
|
||||
*_length = min_c(ioSize, *_length);
|
||||
|
||||
status_t result;
|
||||
OpenState* state = cookie != NULL ? cookie->fOpenState : fOpenState;
|
||||
while (size < *_length && !*eof) {
|
||||
uint32 len = *_length - size;
|
||||
result = ReadFile(cookie, state, pos + size, &len,
|
||||
reinterpret_cast<char*>(buffer) + size, eof);
|
||||
if (result != B_OK) {
|
||||
if (size == 0)
|
||||
return result;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
size += len;
|
||||
}
|
||||
|
||||
*_length = size;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(_length != NULL);
|
||||
|
||||
bool eof = false;
|
||||
if ((cookie->fMode & O_NOCACHE) != 0)
|
||||
return ReadDirect(cookie, pos, buffer, _length, &eof);
|
||||
return file_cache_read(fFileCache, cookie, pos, buffer, _length);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer,
|
||||
size_t* _length)
|
||||
{
|
||||
ASSERT(cookie != NULL || fOpenState != NULL);
|
||||
ASSERT(_buffer != NULL);
|
||||
ASSERT(_length != NULL);
|
||||
|
||||
uint32 size = 0;
|
||||
const char* buffer = reinterpret_cast<const char*>(_buffer);
|
||||
|
||||
uint32 ioSize = fFileSystem->Root()->IOSize();
|
||||
*_length = min_c(ioSize, *_length);
|
||||
|
||||
bool attribute = false;
|
||||
OpenState* state = fOpenState;
|
||||
if (cookie != NULL) {
|
||||
attribute = cookie->fOpenState->fInfo.fHandle != fInfo.fHandle;
|
||||
state = cookie->fOpenState;
|
||||
}
|
||||
|
||||
if (!attribute) {
|
||||
ReadLocker _(fWriteLock);
|
||||
fWriteDirty = true;
|
||||
}
|
||||
|
||||
while (size < *_length) {
|
||||
uint32 len = *_length - size;
|
||||
status_t result = WriteFile(cookie, state, pos + size, &len,
|
||||
buffer + size, attribute);
|
||||
if (result != B_OK) {
|
||||
if (size == 0)
|
||||
return result;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
size += len;
|
||||
}
|
||||
|
||||
*_length = size;
|
||||
|
||||
fMetaCache.GrowFile(size + pos);
|
||||
fFileSystem->Root()->MakeInfoInvalid();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
|
||||
size_t* _length)
|
||||
{
|
||||
ASSERT(cookie != NULL);
|
||||
ASSERT(_buffer != NULL);
|
||||
ASSERT(_length != NULL);
|
||||
|
||||
struct stat st;
|
||||
status_t result = Stat(&st);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if ((cookie->fMode & O_APPEND) != 0)
|
||||
pos = st.st_size;
|
||||
|
||||
uint64 fileSize = max_c(st.st_size, pos + *_length);
|
||||
fMaxFileSize = max_c(fMaxFileSize, fileSize);
|
||||
|
||||
if ((cookie->fMode & O_NOCACHE) != 0) {
|
||||
WriteDirect(cookie, pos, _buffer, _length);
|
||||
Commit();
|
||||
}
|
||||
|
||||
result = file_cache_set_size(fFileCache, fileSize);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
return file_cache_write(fFileCache, cookie, pos, _buffer, _length);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::Commit()
|
||||
{
|
||||
WriteLocker _(fWriteLock);
|
||||
|
||||
if (!fWriteDirty)
|
||||
return B_OK;
|
||||
status_t result = CommitWrites();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
fWriteDirty = false;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
UsePrivateHeaders shared ;
|
||||
|
||||
KernelAddon nfs4 :
|
||||
Cookie.cpp
|
||||
Connection.cpp
|
||||
Delegation.cpp
|
||||
DirectoryCache.cpp
|
||||
FileInfo.cpp
|
||||
FileSystem.cpp
|
||||
IdMap.cpp
|
||||
Inode.cpp
|
||||
InodeDir.cpp
|
||||
InodeRegular.cpp
|
||||
kernel_interface.cpp
|
||||
MetadataCache.cpp
|
||||
NFS4Inode.cpp
|
||||
NFS4Object.cpp
|
||||
NFS4Server.cpp
|
||||
OpenState.cpp
|
||||
ReplyBuilder.cpp
|
||||
ReplyInterpreter.cpp
|
||||
Request.cpp
|
||||
RequestBuilder.cpp
|
||||
RequestInterpreter.cpp
|
||||
RootInode.cpp
|
||||
RPCAuth.cpp
|
||||
RPCCall.cpp
|
||||
RPCCallback.cpp
|
||||
RPCCallbackReply.cpp
|
||||
RPCCallbackRequest.cpp
|
||||
RPCCallbackServer.cpp
|
||||
RPCReply.cpp
|
||||
RPCServer.cpp
|
||||
VnodeToInode.cpp
|
||||
WorkQueue.cpp
|
||||
XDR.cpp
|
||||
;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "MetadataCache.h"
|
||||
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
MetadataCache::MetadataCache(Inode* inode)
|
||||
:
|
||||
fExpire(0),
|
||||
fForceValid(false),
|
||||
fInode(inode),
|
||||
fInited(false)
|
||||
{
|
||||
ASSERT(inode != NULL);
|
||||
mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
MetadataCache::~MetadataCache()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MetadataCache::GetStat(struct stat* st)
|
||||
{
|
||||
ASSERT(st != NULL);
|
||||
|
||||
MutexLocker _(fLock);
|
||||
if (fForceValid || fExpire > time(NULL)) {
|
||||
// Do not touch other members of struct stat
|
||||
st->st_size = fStatCache.st_size;
|
||||
st->st_mode = fStatCache.st_mode;
|
||||
st->st_nlink = fStatCache.st_nlink;
|
||||
st->st_uid = fStatCache.st_uid;
|
||||
st->st_gid = fStatCache.st_gid;
|
||||
st->st_atim = fStatCache.st_atim;
|
||||
st->st_ctim = fStatCache.st_ctim;
|
||||
st->st_crtim = fStatCache.st_crtim;
|
||||
st->st_mtim = fStatCache.st_mtim;
|
||||
st->st_blksize = fStatCache.st_blksize;
|
||||
st->st_blocks = fStatCache.st_blocks;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MetadataCache::SetStat(const struct stat& st)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
if (fInited)
|
||||
NotifyChanges(&fStatCache, &st);
|
||||
|
||||
fStatCache = st;
|
||||
fExpire = time(NULL) + kExpirationTime;
|
||||
fInited = true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MetadataCache::GrowFile(size_t newSize)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
fStatCache.st_size = max_c(newSize, fStatCache.st_size);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MetadataCache::GetAccess(uid_t uid, uint32* allowed)
|
||||
{
|
||||
ASSERT(allowed != NULL);
|
||||
|
||||
MutexLocker _(fLock);
|
||||
AVLTreeMap<uid_t, AccessEntry>::Iterator it = fAccessCache.Find(uid);
|
||||
if (!it.HasCurrent())
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
if (!fForceValid)
|
||||
it.CurrentValuePointer()->fForceValid = false;
|
||||
|
||||
if (!it.Current().fForceValid && it.Current().fExpire < time(NULL)) {
|
||||
it.Remove();
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
*allowed = it.Current().fAllowed;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MetadataCache::SetAccess(uid_t uid, uint32 allowed)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
AVLTreeMap<uid_t, AccessEntry>::Iterator it = fAccessCache.Find(uid);
|
||||
if (it.HasCurrent())
|
||||
it.Remove();
|
||||
|
||||
AccessEntry entry;
|
||||
entry.fAllowed = allowed;
|
||||
entry.fExpire = time(NULL) + kExpirationTime;
|
||||
entry.fForceValid = fForceValid;
|
||||
|
||||
fAccessCache.Insert(uid, entry);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MetadataCache::LockValid()
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
if (fForceValid || fExpire > time(NULL)) {
|
||||
fForceValid = true;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MetadataCache::UnlockValid()
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
fExpire = time(NULL) + kExpirationTime;
|
||||
fForceValid = false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MetadataCache::NotifyChanges(const struct stat* oldStat,
|
||||
const struct stat* newStat)
|
||||
{
|
||||
ASSERT(oldStat != NULL);
|
||||
ASSERT(newStat != NULL);
|
||||
|
||||
uint32 flags = 0;
|
||||
if (oldStat->st_size != newStat->st_size)
|
||||
flags |= B_STAT_SIZE;
|
||||
if (oldStat->st_mode != newStat->st_mode)
|
||||
flags |= B_STAT_MODE;
|
||||
if (oldStat->st_uid != newStat->st_uid)
|
||||
flags |= B_STAT_UID;
|
||||
if (oldStat->st_gid != newStat->st_gid)
|
||||
flags |= B_STAT_GID;
|
||||
|
||||
if (memcmp(&oldStat->st_atim, &newStat->st_atim,
|
||||
sizeof(struct timespec) == 0))
|
||||
flags |= B_STAT_ACCESS_TIME;
|
||||
|
||||
if (memcmp(&oldStat->st_ctim, &newStat->st_ctim,
|
||||
sizeof(struct timespec) == 0))
|
||||
flags |= B_STAT_CHANGE_TIME;
|
||||
|
||||
if (memcmp(&oldStat->st_crtim, &newStat->st_crtim,
|
||||
sizeof(struct timespec) == 0))
|
||||
flags |= B_STAT_CREATION_TIME;
|
||||
|
||||
if (memcmp(&oldStat->st_mtim, &newStat->st_mtim,
|
||||
sizeof(struct timespec) == 0))
|
||||
flags |= B_STAT_MODIFICATION_TIME;
|
||||
|
||||
notify_stat_changed(fInode->GetFileSystem()->DevId(), fInode->ID(), flags);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef METADATACACHE_H
|
||||
#define METADATACACHE_H
|
||||
|
||||
|
||||
#include <fs_interface.h>
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <util/AVLTreeMap.h>
|
||||
|
||||
|
||||
class Inode;
|
||||
|
||||
struct AccessEntry {
|
||||
time_t fExpire;
|
||||
bool fForceValid;
|
||||
|
||||
uint32 fAllowed;
|
||||
};
|
||||
|
||||
class MetadataCache {
|
||||
public:
|
||||
MetadataCache(Inode* inode);
|
||||
~MetadataCache();
|
||||
|
||||
status_t GetStat(struct stat* st);
|
||||
void SetStat(const struct stat& st);
|
||||
void GrowFile(size_t newSize);
|
||||
|
||||
status_t GetAccess(uid_t uid, uint32* allowed);
|
||||
void SetAccess(uid_t uid, uint32 allowed);
|
||||
|
||||
status_t LockValid();
|
||||
void UnlockValid();
|
||||
|
||||
inline void InvalidateStat();
|
||||
inline void InvalidateAccess();
|
||||
|
||||
inline void Invalidate();
|
||||
|
||||
static const time_t kExpirationTime = 60;
|
||||
|
||||
protected:
|
||||
void NotifyChanges(const struct stat* oldStat,
|
||||
const struct stat* newStat);
|
||||
|
||||
private:
|
||||
struct stat fStatCache;
|
||||
time_t fExpire;
|
||||
bool fForceValid;
|
||||
|
||||
Inode* fInode;
|
||||
bool fInited;
|
||||
|
||||
AVLTreeMap<uid_t, AccessEntry> fAccessCache;
|
||||
|
||||
mutex fLock;
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
MetadataCache::InvalidateStat()
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
if (!fForceValid)
|
||||
fExpire = 0;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
MetadataCache::InvalidateAccess()
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
if (!fForceValid)
|
||||
fAccessCache.MakeEmpty();
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
MetadataCache::Invalidate()
|
||||
{
|
||||
InvalidateStat();
|
||||
InvalidateAccess();
|
||||
}
|
||||
|
||||
|
||||
#endif // METADATACACHE_H
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef NFS4DEFS_H
|
||||
#define NFS4DEFS_H
|
||||
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
enum Procedure {
|
||||
ProcNull = 0,
|
||||
ProcCompound = 1
|
||||
};
|
||||
|
||||
enum CallbackProcedure {
|
||||
CallbackProcNull = 0,
|
||||
CallbackProcCompound = 1
|
||||
};
|
||||
|
||||
enum CallbackOpcode {
|
||||
OpCallbackGetAttr = 3,
|
||||
OpCallbackRecall = 4
|
||||
};
|
||||
|
||||
enum Opcode {
|
||||
OpAccess = 3,
|
||||
OpClose = 4,
|
||||
OpCommit = 5,
|
||||
OpCreate = 6,
|
||||
OpDelegReturn = 8,
|
||||
OpGetAttr = 9,
|
||||
OpGetFH = 10,
|
||||
OpLink = 11,
|
||||
OpLock = 12,
|
||||
OpLockT = 13,
|
||||
OpLockU = 14,
|
||||
OpLookUp = 15,
|
||||
OpLookUpUp = 16,
|
||||
OpNverify = 17,
|
||||
OpOpen = 18,
|
||||
OpOpenAttrDir = 19,
|
||||
OpOpenConfirm = 20,
|
||||
OpPutFH = 22,
|
||||
OpPutRootFH = 24,
|
||||
OpRead = 25,
|
||||
OpReadDir = 26,
|
||||
OpReadLink = 27,
|
||||
OpRemove = 28,
|
||||
OpRename = 29,
|
||||
OpRenew = 30,
|
||||
OpSaveFH = 32,
|
||||
OpSetAttr = 34,
|
||||
OpSetClientID = 35,
|
||||
OpSetClientIDConfirm = 36,
|
||||
OpVerify = 37,
|
||||
OpWrite = 38,
|
||||
OpReleaseLockOwner = 39
|
||||
};
|
||||
|
||||
enum Access {
|
||||
ACCESS4_READ = 0x00000001,
|
||||
ACCESS4_LOOKUP = 0x00000002,
|
||||
ACCESS4_MODIFY = 0x00000004,
|
||||
ACCESS4_EXTEND = 0x00000008,
|
||||
ACCESS4_DELETE = 0x00000010,
|
||||
ACCESS4_EXECUTE = 0x00000020
|
||||
};
|
||||
|
||||
enum Attribute {
|
||||
// Mandatory Attributes
|
||||
FATTR4_SUPPORTED_ATTRS = 0,
|
||||
FATTR4_TYPE = 1,
|
||||
FATTR4_FH_EXPIRE_TYPE = 2,
|
||||
FATTR4_CHANGE = 3,
|
||||
FATTR4_SIZE = 4,
|
||||
FATTR4_LINK_SUPPORT = 5,
|
||||
FATTR4_SYMLINK_SUPPORT = 6,
|
||||
FATTR4_NAMED_ATTR = 7,
|
||||
FATTR4_FSID = 8,
|
||||
FATTR4_UNIQUE_HANDLES = 9,
|
||||
FATTR4_LEASE_TIME = 10,
|
||||
FATTR4_RDATTR_ERROR = 11,
|
||||
FATTR4_FILEHANDLE = 19,
|
||||
|
||||
// Recommended Attributes
|
||||
FATTR4_ACL = 12,
|
||||
FATTR4_ACLSUPPORT = 13,
|
||||
FATTR4_ARCHIVE = 14,
|
||||
FATTR4_CANSETTIME = 15,
|
||||
FATTR4_CASE_INSENSITIVE = 16,
|
||||
FATTR4_CASE_PRESERVING = 17,
|
||||
FATTR4_CHOWN_RESTRICTED = 18,
|
||||
FATTR4_FILEID = 20,
|
||||
FATTR4_FILES_AVAIL = 21,
|
||||
FATTR4_FILES_FREE = 22,
|
||||
FATTR4_FILES_TOTAL = 23,
|
||||
FATTR4_FS_LOCATIONS = 24,
|
||||
FATTR4_HIDDEN = 25,
|
||||
FATTR4_HOMOGENEOUS = 26,
|
||||
FATTR4_MAXFILESIZE = 27,
|
||||
FATTR4_MAXLINK = 28,
|
||||
FATTR4_MAXNAME = 29,
|
||||
FATTR4_MAXREAD = 30,
|
||||
FATTR4_MAXWRITE = 31,
|
||||
FATTR4_MIMETYPE = 32,
|
||||
FATTR4_MODE = 33,
|
||||
FATTR4_NO_TRUNC = 34,
|
||||
FATTR4_NUMLINKS = 35,
|
||||
FATTR4_OWNER = 36,
|
||||
FATTR4_OWNER_GROUP = 37,
|
||||
FATTR4_QUOTA_AVAIL_HARD = 38,
|
||||
FATTR4_QUOTA_AVAIL_SOFT = 39,
|
||||
FATTR4_QUOTA_USED = 40,
|
||||
FATTR4_RAWDEV = 41,
|
||||
FATTR4_SPACE_AVAIL = 42,
|
||||
FATTR4_SPACE_FREE = 43,
|
||||
FATTR4_SPACE_TOTAL = 44,
|
||||
FATTR4_SPACE_USED = 45,
|
||||
FATTR4_SYSTEM = 46,
|
||||
FATTR4_TIME_ACCESS = 47,
|
||||
FATTR4_TIME_ACCESS_SET = 48,
|
||||
FATTR4_TIME_BACKUP = 49,
|
||||
FATTR4_TIME_CREATE = 50,
|
||||
FATTR4_TIME_DELTA = 51,
|
||||
FATTR4_TIME_METADATA = 52,
|
||||
FATTR4_TIME_MODIFY = 53,
|
||||
FATTR4_TIME_MODIFY_SET = 54,
|
||||
FATTR4_MOUNTED_ON_FILEID = 55,
|
||||
FATTR4_MAXIMUM_ATTR_ID
|
||||
};
|
||||
|
||||
enum CallbackAttr {
|
||||
CallbackAttrSize = 1,
|
||||
CallbackAttrChange = 2
|
||||
};
|
||||
|
||||
static inline bool sIsAttrSet(Attribute attr, const uint32* bitmap,
|
||||
uint32 count)
|
||||
{
|
||||
if ((uint32)attr / 32 >= count)
|
||||
return false;
|
||||
|
||||
return (bitmap[attr / 32] & 1 << attr % 32) != 0;
|
||||
}
|
||||
|
||||
|
||||
enum FileType {
|
||||
NF4REG = 1, /* Regular File */
|
||||
NF4DIR = 2, /* Directory */
|
||||
NF4BLK = 3, /* Special File - block device */
|
||||
NF4CHR = 4, /* Special File - character device */
|
||||
NF4LNK = 5, /* Symbolic Link */
|
||||
NF4SOCK = 6, /* Special File - socket */
|
||||
NF4FIFO = 7, /* Special File - fifo */
|
||||
NF4ATTRDIR = 8, /* Attribute Directory */
|
||||
NF4NAMEDATTR = 9 /* Named Attribute */
|
||||
};
|
||||
|
||||
static const mode_t sNFSFileTypeToHaiku[] = {
|
||||
S_IFREG, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO,
|
||||
S_IFDIR, S_IFREG
|
||||
};
|
||||
|
||||
enum FileHandleExpiryType {
|
||||
FH4_PERSISTENT = 0x00,
|
||||
FH4_NOEXPIRE_WITH_OPEN = 0x01,
|
||||
FH4_VOLATILE_ANY = 0x02,
|
||||
FH4_VOL_MIGRATION = 0x04,
|
||||
FH4_VOL_RENAME = 0x08
|
||||
};
|
||||
|
||||
enum OpenAccess {
|
||||
OPEN4_SHARE_ACCESS_READ = 1,
|
||||
OPEN4_SHARE_ACCESS_WRITE = 2,
|
||||
OPEN4_SHARE_ACCESS_BOTH = 3
|
||||
};
|
||||
|
||||
|
||||
static inline OpenAccess
|
||||
sModeToAccess(int mode)
|
||||
{
|
||||
switch (mode & O_RWMASK) {
|
||||
case O_RDONLY:
|
||||
return OPEN4_SHARE_ACCESS_READ;
|
||||
case O_WRONLY:
|
||||
return OPEN4_SHARE_ACCESS_WRITE;
|
||||
case O_RDWR:
|
||||
return OPEN4_SHARE_ACCESS_BOTH;
|
||||
}
|
||||
|
||||
return OPEN4_SHARE_ACCESS_READ;
|
||||
}
|
||||
|
||||
|
||||
enum OpenCreate {
|
||||
OPEN4_NOCREATE = 0,
|
||||
OPEN4_CREATE = 1
|
||||
};
|
||||
|
||||
enum OpenCreateHow {
|
||||
UNCHECKED4 = 0,
|
||||
GUARDED4 = 1,
|
||||
EXCLUSIVE4 = 2
|
||||
};
|
||||
|
||||
enum OpenClaim {
|
||||
CLAIM_NULL = 0,
|
||||
CLAIM_PREVIOUS = 1,
|
||||
CLAIM_DELEGATE_CUR = 2,
|
||||
CLAIM_DELEGATE_PREV = 3
|
||||
};
|
||||
|
||||
enum OpenDelegation {
|
||||
OPEN_DELEGATE_NONE = 0,
|
||||
OPEN_DELEGATE_READ = 1,
|
||||
OPEN_DELEGATE_WRITE = 2
|
||||
};
|
||||
|
||||
struct OpenDelegationData {
|
||||
OpenDelegation fType;
|
||||
|
||||
uint32 fStateSeq;
|
||||
uint32 fStateID[3];
|
||||
|
||||
bool fRecall;
|
||||
uint64 fSpaceLimit;
|
||||
};
|
||||
|
||||
enum OpenFlags {
|
||||
OPEN4_RESULT_CONFIRM = 2,
|
||||
OPEN4_RESULT_LOCKTYPE_POSIX = 4
|
||||
};
|
||||
|
||||
enum {
|
||||
NFS_LIMIT_SIZE = 1,
|
||||
NFS_LIMIT_BLOCKS = 2
|
||||
};
|
||||
|
||||
struct ChangeInfo {
|
||||
bool fAtomic;
|
||||
uint64 fBefore;
|
||||
uint64 fAfter;
|
||||
};
|
||||
|
||||
enum WriteStable {
|
||||
UNSTABLE4 = 0,
|
||||
DATA_SYNC4 = 1,
|
||||
FILE_SYNC4 = 2
|
||||
};
|
||||
|
||||
enum LockType {
|
||||
READ_LT = 1,
|
||||
WRITE_LT = 2,
|
||||
READW_LT = 3,
|
||||
WRITEW_LT = 4
|
||||
};
|
||||
|
||||
|
||||
static inline LockType
|
||||
sGetLockType(short type, bool wait) {
|
||||
switch (type) {
|
||||
case F_RDLCK: return wait ? READW_LT : READ_LT;
|
||||
case F_WRLCK: return wait ? WRITEW_LT : WRITE_LT;
|
||||
default: return READ_LT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline short
|
||||
sLockTypeToHaiku(LockType type) {
|
||||
switch (type) {
|
||||
case READ_LT:
|
||||
case READW_LT:
|
||||
return F_RDLCK;
|
||||
|
||||
case WRITE_LT:
|
||||
case WRITEW_LT:
|
||||
return F_WRLCK;
|
||||
|
||||
default: return F_UNLCK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum Errors {
|
||||
NFS4_OK = 0,
|
||||
NFS4ERR_PERM = 1,
|
||||
NFS4ERR_NOENT = 2,
|
||||
NFS4ERR_IO = 5,
|
||||
NFS4ERR_NXIO = 6,
|
||||
NFS4ERR_ACCESS = 13,
|
||||
NFS4ERR_EXIST = 17,
|
||||
NFS4ERR_XDEV = 18,
|
||||
NFS4ERR_NOTDIR = 20,
|
||||
NFS4ERR_ISDIR = 21,
|
||||
NFS4ERR_INVAL = 22,
|
||||
NFS4ERR_FBIG = 27,
|
||||
NFS4ERR_NOSPC = 28,
|
||||
NFS4ERR_ROFS = 30,
|
||||
NFS4ERR_MLINK = 31,
|
||||
NFS4ERR_NAMETOOLONG = 63,
|
||||
NFS4ERR_NOTEMPTY = 66,
|
||||
NFS4ERR_DQUOT = 69,
|
||||
NFS4ERR_STALE = 70,
|
||||
NFS4ERR_BADHANDLE = 10001,
|
||||
NFS4ERR_BAD_COOKIE = 10003,
|
||||
NFS4ERR_NOTSUPP = 10004,
|
||||
NFS4ERR_TOOSMALL = 10005,
|
||||
NFS4ERR_SERVERFAULT = 10006,
|
||||
NFS4ERR_BADTYPE = 10007,
|
||||
NFS4ERR_DELAY = 10008,
|
||||
NFS4ERR_SAME = 10009,
|
||||
NFS4ERR_DENIED = 10010,
|
||||
NFS4ERR_EXPIRED = 10011,
|
||||
NFS4ERR_LOCKED = 10012,
|
||||
NFS4ERR_GRACE = 10013,
|
||||
NFS4ERR_FHEXPIRED = 10014,
|
||||
NFS4ERR_SHARE_DENIED = 10015,
|
||||
NFS4ERR_WRONGSEC = 10016,
|
||||
NFS4ERR_CLID_INUSE = 10017,
|
||||
NFS4ERR_RESOURCE = 10018,
|
||||
NFS4ERR_MOVED = 10019,
|
||||
NFS4ERR_NOFILEHANDLE = 10020,
|
||||
NFS4ERR_MINOR_VERS_MISMATCH = 10021,
|
||||
NFS4ERR_STALE_CLIENTID = 10022,
|
||||
NFS4ERR_STALE_STATEID = 10023,
|
||||
NFS4ERR_OLD_STATEID = 10024,
|
||||
NFS4ERR_BAD_STATEID = 10025,
|
||||
NFS4ERR_BAD_SEQID = 10026,
|
||||
NFS4ERR_NOT_SAME = 10027,
|
||||
NFS4ERR_LOCK_RANGE = 10028,
|
||||
NFS4ERR_SYMLINK = 10029,
|
||||
NFS4ERR_RESTOREFH = 10030,
|
||||
NFS4ERR_LEASE_MOVED = 10031,
|
||||
NFS4ERR_ATTRNOTSUPP = 10032,
|
||||
NFS4ERR_NO_GRACE = 10033,
|
||||
NFS4ERR_RECLAIM_BAD = 10034,
|
||||
NFS4ERR_RECLAIM_CONFLICT = 10035,
|
||||
NFS4ERR_BADXDR = 10036,
|
||||
NFS4ERR_LOCKS_HELD = 10037,
|
||||
NFS4ERR_OPENMODE = 10038,
|
||||
NFS4ERR_BADOWNER = 10039,
|
||||
NFS4ERR_BADCHAR = 10040,
|
||||
NFS4ERR_BADNAME = 10041,
|
||||
NFS4ERR_BAD_RANGE = 10042,
|
||||
NFS4ERR_LOCK_NOTSUPP = 10043,
|
||||
NFS4ERR_OP_ILLEGAL = 10044,
|
||||
NFS4ERR_DEADLOCK = 10045,
|
||||
NFS4ERR_FILE_OPEN = 10046,
|
||||
NFS4ERR_ADMIN_REVOKED = 10047,
|
||||
NFS4ERR_CB_PATH_DOWN = 10048
|
||||
};
|
||||
|
||||
|
||||
static inline bigtime_t
|
||||
sSecToBigTime(uint32 sec)
|
||||
{
|
||||
return static_cast<bigtime_t>(sec) * 1000000;
|
||||
}
|
||||
|
||||
|
||||
#endif // NFS4DEFS_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef NFS4INODE_H
|
||||
#define NFS4INODE_H
|
||||
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "Cookie.h"
|
||||
#include "FileInfo.h"
|
||||
#include "FileSystem.h"
|
||||
#include "NFS4Object.h"
|
||||
#include "ReplyInterpreter.h"
|
||||
|
||||
|
||||
class NFS4Inode : public NFS4Object {
|
||||
public:
|
||||
status_t GetChangeInfo(uint64* change, bool attrDir = false);
|
||||
status_t ReadLink(void* buffer, size_t* length);
|
||||
|
||||
protected:
|
||||
status_t Access(uint32* allowed);
|
||||
|
||||
status_t CommitWrites();
|
||||
|
||||
status_t LookUp(const char* name, uint64* change, uint64* fileID,
|
||||
FileHandle* handle, bool parent = false);
|
||||
|
||||
status_t Link(Inode* dir, const char* name,
|
||||
ChangeInfo* changeInfo);
|
||||
|
||||
static status_t RenameNode(Inode* from, Inode* to, const char* fromName,
|
||||
const char* toName, ChangeInfo* fromChange,
|
||||
ChangeInfo* toChange, uint64* fileID,
|
||||
bool attribute = false);
|
||||
|
||||
status_t GetStat(AttrValue** values, uint32* count,
|
||||
OpenAttrCookie* attr = NULL);
|
||||
status_t WriteStat(OpenState* state, AttrValue* attrs,
|
||||
uint32 attrCount);
|
||||
|
||||
status_t CreateFile(const char* name, int mode, int perms,
|
||||
OpenState* state, ChangeInfo* changeInfo,
|
||||
uint64* fileID, FileHandle* handle,
|
||||
OpenDelegationData* delegation);
|
||||
status_t OpenFile(OpenState* state, int mode,
|
||||
OpenDelegationData* delegation);
|
||||
|
||||
status_t OpenAttr(OpenState* state, const char* name, int mode,
|
||||
OpenDelegationData* delegation, bool create);
|
||||
|
||||
status_t ReadFile(OpenStateCookie* cookie, OpenState* state,
|
||||
uint64 position, uint32* length, void* buffer,
|
||||
bool* eof);
|
||||
status_t WriteFile(OpenStateCookie* cookie, OpenState* state,
|
||||
uint64 position, uint32* length,
|
||||
const void* buffer, bool commit = false);
|
||||
|
||||
status_t CreateObject(const char* name, const char* path,
|
||||
int mode, FileType type, ChangeInfo* changeInfo,
|
||||
uint64* fileID, FileHandle* handle,
|
||||
bool parent = false);
|
||||
status_t RemoveObject(const char* name, FileType type,
|
||||
ChangeInfo* changeInfo, uint64* fileID);
|
||||
|
||||
status_t ReadDirOnce(DirEntry** dirents, uint32* count,
|
||||
OpenDirCookie* cookie, bool* eof, uint64* change,
|
||||
uint64* dirCookie, uint64* dirCookieVerf,
|
||||
bool attribute);
|
||||
|
||||
status_t OpenAttrDir(FileHandle* handle);
|
||||
|
||||
status_t TestLock(OpenFileCookie* cookie, LockType* type,
|
||||
uint64* position, uint64* length, bool& conflict);
|
||||
status_t AcquireLock(OpenFileCookie* cookie, LockInfo* lockInfo,
|
||||
bool wait);
|
||||
status_t ReleaseLock(OpenFileCookie* cookie, LockInfo* lockInfo);
|
||||
};
|
||||
|
||||
|
||||
#endif // NFS4INODE_H
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "Cookie.h"
|
||||
#include "FileSystem.h"
|
||||
#include "NFS4Object.h"
|
||||
#include "OpenState.h"
|
||||
#include "Request.h"
|
||||
|
||||
|
||||
bool
|
||||
NFS4Object::HandleErrors(uint32 nfs4Error, RPC::Server* serv,
|
||||
OpenStateCookie* cookie, OpenState* state, uint32* sequence)
|
||||
{
|
||||
uint32 leaseTime;
|
||||
|
||||
// No request send by the client should cause any of the following errors.
|
||||
ASSERT(nfs4Error != NFS4ERR_CLID_INUSE);
|
||||
ASSERT(nfs4Error != NFS4ERR_NOFILEHANDLE);
|
||||
ASSERT(nfs4Error != NFS4ERR_BAD_STATEID);
|
||||
ASSERT(nfs4Error != NFS4ERR_RESTOREFH);
|
||||
ASSERT(nfs4Error != NFS4ERR_LOCKS_HELD);
|
||||
ASSERT(nfs4Error != NFS4ERR_OP_ILLEGAL);
|
||||
|
||||
if (cookie != NULL)
|
||||
state = cookie->fOpenState;
|
||||
|
||||
switch (nfs4Error) {
|
||||
case NFS4_OK:
|
||||
return false;
|
||||
|
||||
// retransmission of CLOSE caused seqid to fall back
|
||||
case NFS4ERR_BAD_SEQID:
|
||||
ASSERT(sequence != NULL);
|
||||
(*sequence)++;
|
||||
return true;
|
||||
|
||||
// server needs more time, we need to wait
|
||||
case NFS4ERR_LOCKED:
|
||||
case NFS4ERR_DELAY:
|
||||
if (sequence != NULL)
|
||||
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
|
||||
|
||||
if (cookie == NULL) {
|
||||
snooze_etc(sSecToBigTime(5), B_SYSTEM_TIMEBASE,
|
||||
B_RELATIVE_TIMEOUT);
|
||||
|
||||
if (sequence != NULL)
|
||||
*sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((cookie->fMode & O_NONBLOCK) == 0) {
|
||||
status_t result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
|
||||
B_RELATIVE_TIMEOUT, sSecToBigTime(5));
|
||||
|
||||
if (sequence != NULL)
|
||||
*sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
|
||||
if (result != B_TIMED_OUT) {
|
||||
if (result == B_OK)
|
||||
release_sem(cookie->fSnoozeCancel);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sequence != NULL)
|
||||
*sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
return false;
|
||||
|
||||
// server is in grace period, we need to wait
|
||||
case NFS4ERR_GRACE:
|
||||
leaseTime = fFileSystem->NFSServer()->LeaseTime();
|
||||
if (sequence != NULL)
|
||||
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
|
||||
|
||||
if (cookie == NULL) {
|
||||
snooze_etc(sSecToBigTime(leaseTime) / 3, B_SYSTEM_TIMEBASE,
|
||||
B_RELATIVE_TIMEOUT);
|
||||
if (sequence != NULL)
|
||||
*sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((cookie->fMode & O_NONBLOCK) == 0) {
|
||||
status_t result = acquire_sem_etc(cookie->fSnoozeCancel, 1,
|
||||
B_RELATIVE_TIMEOUT, sSecToBigTime(leaseTime) / 3);
|
||||
|
||||
if (sequence != NULL)
|
||||
*sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
|
||||
if (result != B_TIMED_OUT) {
|
||||
if (result == B_OK)
|
||||
release_sem(cookie->fSnoozeCancel);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sequence != NULL)
|
||||
*sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
return false;
|
||||
|
||||
// server has rebooted, reclaim share and try again
|
||||
case NFS4ERR_STALE_CLIENTID:
|
||||
case NFS4ERR_STALE_STATEID:
|
||||
if (state != NULL) {
|
||||
if (sequence != NULL)
|
||||
fFileSystem->OpenOwnerSequenceUnlock(*sequence);
|
||||
|
||||
fFileSystem->NFSServer()->ServerRebooted(state->fClientID);
|
||||
if (sequence != NULL)
|
||||
*sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// File Handle has expired, is invalid or the node has been deleted
|
||||
case NFS4ERR_NOFILEHANDLE:
|
||||
case NFS4ERR_BADHANDLE:
|
||||
case NFS4ERR_FHEXPIRED:
|
||||
case NFS4ERR_STALE:
|
||||
if (fInfo.UpdateFileHandles(fFileSystem) == B_OK)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
// filesystem has been moved
|
||||
case NFS4ERR_LEASE_MOVED:
|
||||
case NFS4ERR_MOVED:
|
||||
fFileSystem->Migrate(serv);
|
||||
return true;
|
||||
|
||||
// lease has expired
|
||||
case NFS4ERR_EXPIRED:
|
||||
if (state != NULL) {
|
||||
fFileSystem->NFSServer()->ClientId(state->fClientID, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Object::ConfirmOpen(const FileHandle& fh, OpenState* state,
|
||||
uint32* sequence)
|
||||
{
|
||||
ASSERT(state != NULL);
|
||||
ASSERT(sequence != NULL);
|
||||
|
||||
do {
|
||||
RPC::Server* serv = fFileSystem->Server();
|
||||
Request request(serv, fFileSystem);
|
||||
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fh);
|
||||
req.OpenConfirm(*sequence, state->fStateID, state->fStateSeq);
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
*sequence += IncrementSequence(reply.NFS4Error());
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), serv, NULL, state))
|
||||
continue;
|
||||
|
||||
reply.PutFH();
|
||||
result = reply.OpenConfirm(&state->fStateSeq);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
return B_OK;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
NFS4Object::IncrementSequence(uint32 error)
|
||||
{
|
||||
if (error != NFS4ERR_STALE_CLIENTID && error != NFS4ERR_STALE_STATEID
|
||||
&& error != NFS4ERR_BAD_STATEID && error != NFS4ERR_BAD_SEQID
|
||||
&& error != NFS4ERR_BADXDR && error != NFS4ERR_RESOURCE
|
||||
&& error != NFS4ERR_NOFILEHANDLE)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef NFS4OBJECT_H
|
||||
#define NFS4OBJECT_H
|
||||
|
||||
#include "FileInfo.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "RPCServer.h"
|
||||
|
||||
|
||||
class OpenStateCookie;
|
||||
class OpenState;
|
||||
|
||||
class NFS4Object {
|
||||
public:
|
||||
bool HandleErrors(uint32 nfs4Error, RPC::Server* serv,
|
||||
OpenStateCookie* cookie = NULL,
|
||||
OpenState* state = NULL, uint32* sequence = NULL);
|
||||
|
||||
status_t ConfirmOpen(const FileHandle& fileHandle,
|
||||
OpenState* state, uint32* sequence);
|
||||
|
||||
static uint32 IncrementSequence(uint32 error);
|
||||
|
||||
inline NFS4Object();
|
||||
|
||||
FileInfo fInfo;
|
||||
FileSystem* fFileSystem;
|
||||
};
|
||||
|
||||
|
||||
inline
|
||||
NFS4Object::NFS4Object()
|
||||
:
|
||||
fFileSystem(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#endif // NFS4OBJECT_H
|
||||
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Inode.h"
|
||||
#include "NFS4Server.h"
|
||||
#include "Request.h"
|
||||
#include "WorkQueue.h"
|
||||
|
||||
|
||||
NFS4Server::NFS4Server(RPC::Server* serv)
|
||||
:
|
||||
fThreadCancel(true),
|
||||
fWaitCancel(create_sem(0, NULL)),
|
||||
fLeaseTime(0),
|
||||
fClientIdLastUse(0),
|
||||
fUseCount(0),
|
||||
fServer(serv)
|
||||
{
|
||||
ASSERT(serv != NULL);
|
||||
|
||||
mutex_init(&fClientIdLock, NULL);
|
||||
mutex_init(&fFSLock, NULL);
|
||||
mutex_init(&fThreadStartLock, NULL);
|
||||
|
||||
}
|
||||
|
||||
|
||||
NFS4Server::~NFS4Server()
|
||||
{
|
||||
fThreadCancel = true;
|
||||
fUseCount = 0;
|
||||
release_sem(fWaitCancel);
|
||||
status_t result;
|
||||
wait_for_thread(fThread, &result);
|
||||
|
||||
delete_sem(fWaitCancel);
|
||||
mutex_destroy(&fClientIdLock);
|
||||
mutex_destroy(&fFSLock);
|
||||
mutex_destroy(&fThreadStartLock);
|
||||
}
|
||||
|
||||
|
||||
uint64
|
||||
NFS4Server::ServerRebooted(uint64 clientId)
|
||||
{
|
||||
if (clientId != fClientId)
|
||||
return fClientId;
|
||||
|
||||
fClientId = ClientId(clientId, true);
|
||||
|
||||
// reclaim all opened files and held locks from all filesystems
|
||||
MutexLocker _(fFSLock);
|
||||
FileSystem* fs = fFileSystems.Head();
|
||||
while (fs != NULL) {
|
||||
DoublyLinkedList<OpenState>::Iterator iterator
|
||||
= fs->OpenFilesLock().GetIterator();
|
||||
OpenState* current = iterator.Next();
|
||||
while (current != NULL) {
|
||||
current->Reclaim(fClientId);
|
||||
|
||||
current = iterator.Next();
|
||||
}
|
||||
fs->OpenFilesUnlock();
|
||||
|
||||
fs = fFileSystems.GetNext(fs);
|
||||
}
|
||||
|
||||
return fClientId;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
NFS4Server::AddFileSystem(FileSystem* fs)
|
||||
{
|
||||
ASSERT(fs != NULL);
|
||||
|
||||
MutexLocker _(fFSLock);
|
||||
fFileSystems.Add(fs);
|
||||
|
||||
fUseCount += fs->OpenFilesCount();
|
||||
if (fs->OpenFilesCount() > 0)
|
||||
_StartRenewing();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
NFS4Server::RemoveFileSystem(FileSystem* fs)
|
||||
{
|
||||
ASSERT(fs != NULL);
|
||||
|
||||
MutexLocker _(fFSLock);
|
||||
fFileSystems.Remove(fs);
|
||||
fUseCount -= fs->OpenFilesCount();
|
||||
}
|
||||
|
||||
|
||||
uint64
|
||||
NFS4Server::ClientId(uint64 prevId, bool forceNew)
|
||||
{
|
||||
MutexLocker _(fClientIdLock);
|
||||
if ((fUseCount == 0 && fClientIdLastUse + (time_t)LeaseTime() < time(NULL))
|
||||
|| (forceNew && fClientId == prevId)) {
|
||||
|
||||
Request request(fServer, NULL);
|
||||
request.Builder().SetClientID(fServer);
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return fClientId;
|
||||
|
||||
uint64 ver;
|
||||
result = request.Reply().SetClientID(&fClientId, &ver);
|
||||
if (result != B_OK)
|
||||
return fClientId;
|
||||
|
||||
request.Reset();
|
||||
request.Builder().SetClientIDConfirm(fClientId, ver);
|
||||
|
||||
result = request.Send();
|
||||
if (result != B_OK)
|
||||
return fClientId;
|
||||
|
||||
result = request.Reply().SetClientIDConfirm();
|
||||
if (result != B_OK)
|
||||
return fClientId;
|
||||
}
|
||||
|
||||
fClientIdLastUse = time(NULL);
|
||||
return fClientId;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::FileSystemMigrated()
|
||||
{
|
||||
// reclaim all opened files and held locks from all filesystems
|
||||
MutexLocker _(fFSLock);
|
||||
FileSystem* fs = fFileSystems.Head();
|
||||
while (fs != NULL) {
|
||||
fs->Migrate(fServer);
|
||||
fs = fFileSystems.GetNext(fs);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::_GetLeaseTime()
|
||||
{
|
||||
Request request(fServer, NULL);
|
||||
request.Builder().PutRootFH();
|
||||
Attribute attr[] = { FATTR4_LEASE_TIME };
|
||||
request.Builder().GetAttr(attr, sizeof(attr) / sizeof(Attribute));
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
reply.PutRootFH();
|
||||
|
||||
AttrValue* values;
|
||||
uint32 count;
|
||||
result = reply.GetAttr(&values, &count);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
// FATTR4_LEASE_TIME is mandatory
|
||||
if (count < 1 || values[0].fAttribute != FATTR4_LEASE_TIME) {
|
||||
delete[] values;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
fLeaseTime = values[0].fData.fValue32;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::_StartRenewing()
|
||||
{
|
||||
if (!fThreadCancel)
|
||||
return B_OK;
|
||||
|
||||
MutexLocker _(fThreadStartLock);
|
||||
|
||||
if (!fThreadCancel)
|
||||
return B_OK;
|
||||
|
||||
if (fLeaseTime == 0) {
|
||||
status_t result = _GetLeaseTime();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
fThreadCancel = false;
|
||||
fThread = spawn_kernel_thread(&NFS4Server::_RenewalThreadStart,
|
||||
"NFSv4 Renewal", B_NORMAL_PRIORITY, this);
|
||||
if (fThread < B_OK)
|
||||
return fThread;
|
||||
|
||||
status_t result = resume_thread(fThread);
|
||||
if (result != B_OK) {
|
||||
kill_thread(fThread);
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::_Renewal()
|
||||
{
|
||||
while (!fThreadCancel) {
|
||||
// TODO: operations like OPEN, READ, CLOSE, etc also renew leases
|
||||
status_t result = acquire_sem_etc(fWaitCancel, 1,
|
||||
B_RELATIVE_TIMEOUT, sSecToBigTime(fLeaseTime - 2));
|
||||
if (result != B_TIMED_OUT) {
|
||||
if (result == B_OK)
|
||||
release_sem(fWaitCancel);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
uint64 clientId = fClientId;
|
||||
|
||||
if (fUseCount == 0) {
|
||||
MutexLocker _(fFSLock);
|
||||
if (fUseCount == 0) {
|
||||
fThreadCancel = true;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
Request request(fServer, NULL);
|
||||
request.Builder().Renew(clientId);
|
||||
request.Send();
|
||||
|
||||
switch (request.Reply().NFS4Error()) {
|
||||
case NFS4ERR_CB_PATH_DOWN:
|
||||
RecallAll();
|
||||
break;
|
||||
case NFS4ERR_STALE_CLIENTID:
|
||||
ServerRebooted(clientId);
|
||||
break;
|
||||
case NFS4ERR_LEASE_MOVED:
|
||||
FileSystemMigrated();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::_RenewalThreadStart(void* ptr)
|
||||
{
|
||||
ASSERT(ptr != NULL);
|
||||
NFS4Server* server = reinterpret_cast<NFS4Server*>(ptr);
|
||||
return server->_Renewal();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::ProcessCallback(RPC::CallbackRequest* request,
|
||||
Connection* connection)
|
||||
{
|
||||
ASSERT(request != NULL);
|
||||
ASSERT(connection != NULL);
|
||||
|
||||
RequestInterpreter req(request);
|
||||
ReplyBuilder reply(request->XID());
|
||||
|
||||
status_t result;
|
||||
uint32 count = req.OperationCount();
|
||||
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
switch (req.Operation()) {
|
||||
case OpCallbackGetAttr:
|
||||
result = CallbackGetAttr(&req, &reply);
|
||||
break;
|
||||
case OpCallbackRecall:
|
||||
result = CallbackRecall(&req, &reply);
|
||||
break;
|
||||
default:
|
||||
result = B_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (result != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
XDR::WriteStream& stream = reply.Reply()->Stream();
|
||||
connection->Send(stream.Buffer(), stream.Size());
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply)
|
||||
{
|
||||
ASSERT(request != NULL);
|
||||
ASSERT(reply != NULL);
|
||||
|
||||
uint32 stateID[3];
|
||||
uint32 stateSeq;
|
||||
bool truncate;
|
||||
FileHandle handle;
|
||||
|
||||
status_t result = request->Recall(&handle, truncate, &stateSeq, stateID);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
MutexLocker locker(fFSLock);
|
||||
|
||||
Delegation* delegation = NULL;
|
||||
FileSystem* current = fFileSystems.Head();
|
||||
while (current != NULL) {
|
||||
delegation = current->GetDelegation(handle);
|
||||
if (delegation != NULL)
|
||||
break;
|
||||
|
||||
current = fFileSystems.GetNext(current);
|
||||
}
|
||||
locker.Unlock();
|
||||
|
||||
if (delegation == NULL) {
|
||||
reply->Recall(B_FILE_NOT_FOUND);
|
||||
return B_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
|
||||
args->fDelegation = delegation;
|
||||
args->fTruncate = truncate;
|
||||
gWorkQueue->EnqueueJob(DelegationRecall, args);
|
||||
|
||||
reply->Recall(B_OK);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::CallbackGetAttr(RequestInterpreter* request, ReplyBuilder* reply)
|
||||
{
|
||||
ASSERT(request != NULL);
|
||||
ASSERT(reply != NULL);
|
||||
|
||||
FileHandle handle;
|
||||
int mask;
|
||||
|
||||
status_t result = request->GetAttr(&handle, &mask);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
return B_OK;
|
||||
|
||||
MutexLocker locker(fFSLock);
|
||||
|
||||
Delegation* delegation = NULL;
|
||||
FileSystem* current = fFileSystems.Head();
|
||||
while (current != NULL) {
|
||||
delegation = current->GetDelegation(handle);
|
||||
if (delegation != NULL)
|
||||
break;
|
||||
|
||||
current = fFileSystems.GetNext(current);
|
||||
}
|
||||
locker.Unlock();
|
||||
|
||||
if (delegation == NULL) {
|
||||
reply->GetAttr(B_FILE_NOT_FOUND, 0, 0, 0);
|
||||
return B_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
delegation->GetInode()->Stat(&st);
|
||||
|
||||
uint64 change;
|
||||
change = delegation->GetInode()->Change();
|
||||
if (delegation->GetInode()->Dirty())
|
||||
change++;
|
||||
reply->GetAttr(B_OK, mask, st.st_size, change);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::RecallAll()
|
||||
{
|
||||
MutexLocker _(fFSLock);
|
||||
FileSystem* fs = fFileSystems.Head();
|
||||
while (fs != NULL) {
|
||||
DoublyLinkedList<Delegation>& list = fs->DelegationsLock();
|
||||
DoublyLinkedList<Delegation>::Iterator iterator = list.GetIterator();
|
||||
|
||||
Delegation* current = iterator.Next();
|
||||
while (current != NULL) {
|
||||
DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
|
||||
args->fDelegation = current;
|
||||
args->fTruncate = false;
|
||||
gWorkQueue->EnqueueJob(DelegationRecall, args);
|
||||
|
||||
current = iterator.Next();
|
||||
}
|
||||
fs->DelegationsUnlock();
|
||||
|
||||
fs = fFileSystems.GetNext(fs);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef NFS4SERVER_H
|
||||
#define NFS4SERVER_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
#include "ReplyBuilder.h"
|
||||
#include "RequestInterpreter.h"
|
||||
#include "RPCServer.h"
|
||||
|
||||
|
||||
class FileSystem;
|
||||
class OpenFileCookie;
|
||||
|
||||
class NFS4Server : public RPC::ProgramData {
|
||||
public:
|
||||
NFS4Server(RPC::Server* serv);
|
||||
virtual ~NFS4Server();
|
||||
|
||||
uint64 ServerRebooted(uint64 clientId);
|
||||
status_t FileSystemMigrated();
|
||||
|
||||
void AddFileSystem(FileSystem* fs);
|
||||
void RemoveFileSystem(FileSystem* fs);
|
||||
|
||||
inline void IncUsage();
|
||||
inline void DecUsage();
|
||||
|
||||
uint64 ClientId(uint64 prevId = 0, bool forceNew = false);
|
||||
|
||||
inline uint32 LeaseTime();
|
||||
|
||||
virtual status_t ProcessCallback(RPC::CallbackRequest* request,
|
||||
Connection* connection);
|
||||
|
||||
status_t CallbackRecall(RequestInterpreter* request,
|
||||
ReplyBuilder* reply);
|
||||
status_t RecallAll();
|
||||
|
||||
status_t CallbackGetAttr(RequestInterpreter* request,
|
||||
ReplyBuilder* reply);
|
||||
private:
|
||||
status_t _GetLeaseTime();
|
||||
|
||||
status_t _StartRenewing();
|
||||
status_t _Renewal();
|
||||
static status_t _RenewalThreadStart(void* ptr);
|
||||
|
||||
thread_id fThread;
|
||||
bool fThreadCancel;
|
||||
sem_id fWaitCancel;
|
||||
mutex fThreadStartLock;
|
||||
|
||||
uint32 fLeaseTime;
|
||||
|
||||
uint64 fClientId;
|
||||
bool fClientIdInit;
|
||||
time_t fClientIdLastUse;
|
||||
mutex fClientIdLock;
|
||||
|
||||
uint32 fUseCount;
|
||||
DoublyLinkedList<FileSystem> fFileSystems;
|
||||
mutex fFSLock;
|
||||
|
||||
RPC::Server* fServer;
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
NFS4Server::IncUsage()
|
||||
{
|
||||
MutexLocker _(fFSLock);
|
||||
fUseCount++;
|
||||
_StartRenewing();
|
||||
fClientIdLastUse = time(NULL);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
NFS4Server::DecUsage()
|
||||
{
|
||||
MutexLocker _(fFSLock);
|
||||
fClientIdLastUse = time(NULL);
|
||||
fUseCount--;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
NFS4Server::LeaseTime()
|
||||
{
|
||||
if (fLeaseTime == 0)
|
||||
_GetLeaseTime();
|
||||
|
||||
return fLeaseTime;
|
||||
}
|
||||
|
||||
|
||||
#endif // NFS4SERVER_H
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "OpenState.h"
|
||||
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Request.h"
|
||||
#include "WorkQueue.h"
|
||||
|
||||
|
||||
OpenState::OpenState()
|
||||
:
|
||||
fOpened(false),
|
||||
fDelegation(NULL),
|
||||
fLocks(NULL),
|
||||
fLockOwners(NULL)
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
|
||||
mutex_init(&fLocksLock, NULL);
|
||||
mutex_init(&fOwnerLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
OpenState::~OpenState()
|
||||
{
|
||||
if (fOpened)
|
||||
fFileSystem->RemoveOpenFile(this);
|
||||
Close();
|
||||
|
||||
mutex_destroy(&fLock);
|
||||
|
||||
mutex_destroy(&fLocksLock);
|
||||
mutex_destroy(&fOwnerLock);
|
||||
}
|
||||
|
||||
|
||||
LockOwner*
|
||||
OpenState::GetLockOwner(uint32 owner)
|
||||
{
|
||||
LockOwner* current = fLockOwners;
|
||||
while (current != NULL) {
|
||||
if (current->fOwner == owner)
|
||||
return current;
|
||||
|
||||
current = current->fNext;
|
||||
}
|
||||
|
||||
current = new LockOwner(owner);
|
||||
if (current == NULL)
|
||||
return NULL;
|
||||
|
||||
current->fNext = fLockOwners;
|
||||
if (fLockOwners != NULL)
|
||||
fLockOwners->fPrev = current;
|
||||
fLockOwners = current;
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
// Caller must hold fLocksLock
|
||||
void
|
||||
OpenState::AddLock(LockInfo* lock)
|
||||
{
|
||||
lock->fNext = fLocks;
|
||||
fLocks = lock;
|
||||
}
|
||||
|
||||
|
||||
// Caller must hold fLocksLock
|
||||
void
|
||||
OpenState::RemoveLock(LockInfo* lock, LockInfo* prev)
|
||||
{
|
||||
if (prev != NULL)
|
||||
prev->fNext = lock->fNext;
|
||||
else
|
||||
fLocks = lock->fNext;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OpenState::DeleteLock(LockInfo* lock)
|
||||
{
|
||||
MutexLocker _(fOwnerLock);
|
||||
|
||||
LockOwner* owner = lock->fOwner;
|
||||
delete lock;
|
||||
|
||||
if (owner->fUseCount == 0) {
|
||||
if (owner->fPrev)
|
||||
owner->fPrev->fNext = owner->fNext;
|
||||
else
|
||||
fLockOwners = owner->fNext;
|
||||
if (owner->fNext)
|
||||
owner->fNext->fPrev = owner->fPrev;
|
||||
|
||||
_ReleaseLockOwner(owner);
|
||||
delete owner;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OpenState::_ReleaseLockOwner(LockOwner* owner)
|
||||
{
|
||||
ASSERT(owner != NULL);
|
||||
|
||||
do {
|
||||
RPC::Server* server = fFileSystem->Server();
|
||||
Request request(server, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.ReleaseLockOwner(this, owner);
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), server))
|
||||
continue;
|
||||
|
||||
return reply.ReleaseLockOwner();
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OpenState::Reclaim(uint64 newClientID)
|
||||
{
|
||||
if (!fOpened)
|
||||
return B_OK;
|
||||
|
||||
MutexLocker _(fLock);
|
||||
|
||||
if (fClientID == newClientID)
|
||||
return B_OK;
|
||||
fClientID = newClientID;
|
||||
|
||||
_ReclaimOpen(newClientID);
|
||||
_ReclaimLocks(newClientID);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OpenState::_ReclaimOpen(uint64 newClientID)
|
||||
{
|
||||
bool confirm;
|
||||
OpenDelegationData delegation;
|
||||
delegation.fType = OPEN_DELEGATE_NONE;
|
||||
delegation.fRecall = false;
|
||||
|
||||
status_t result;
|
||||
uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
OpenDelegation delegType = fDelegation != NULL ? fDelegation->Type()
|
||||
: OPEN_DELEGATE_NONE;
|
||||
do {
|
||||
RPC::Server* server = fFileSystem->Server();
|
||||
Request request(server, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
req.Open(CLAIM_PREVIOUS, sequence, sModeToAccess(fMode), newClientID,
|
||||
OPEN4_NOCREATE, fFileSystem->OpenOwner(), NULL, NULL, 0, false,
|
||||
delegType);
|
||||
|
||||
result = request.Send();
|
||||
if (result != B_OK) {
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
return result;
|
||||
}
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
sequence += IncrementSequence(reply.NFS4Error());
|
||||
|
||||
if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
|
||||
&& HandleErrors(reply.NFS4Error(), server, NULL, NULL, &sequence)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reply.PutFH();
|
||||
|
||||
result = reply.Open(fStateID, &fStateSeq, &confirm, &delegation);
|
||||
if (result != B_OK) {
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
return result;
|
||||
}
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
if (fDelegation != NULL)
|
||||
fDelegation->SetData(delegation);
|
||||
|
||||
if (delegation.fRecall) {
|
||||
DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs;
|
||||
args->fDelegation = fDelegation;
|
||||
args->fTruncate = false;
|
||||
gWorkQueue->EnqueueJob(DelegationRecall, args);
|
||||
}
|
||||
|
||||
if (confirm)
|
||||
result = ConfirmOpen(fInfo.fHandle, this, &sequence);
|
||||
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OpenState::_ReclaimLocks(uint64 newClientID)
|
||||
{
|
||||
MutexLocker _(fLocksLock);
|
||||
LockInfo* linfo = fLocks;
|
||||
while (linfo != NULL) {
|
||||
MutexLocker locker(linfo->fOwner->fLock);
|
||||
|
||||
if (linfo->fOwner->fClientId != newClientID) {
|
||||
memset(linfo->fOwner->fStateId, 0, sizeof(linfo->fOwner->fStateId));
|
||||
linfo->fOwner->fClientId = newClientID;
|
||||
}
|
||||
|
||||
uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
do {
|
||||
RPC::Server* server = fFileSystem->Server();
|
||||
Request request(server, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
req.Lock(this, linfo, &sequence, true);
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK) {
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
break;
|
||||
}
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
sequence += IncrementSequence(reply.NFS4Error());
|
||||
|
||||
if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID
|
||||
&& reply.NFS4Error() != NFS4ERR_STALE_STATEID
|
||||
&& HandleErrors(reply.NFS4Error(), server, NULL, NULL,
|
||||
&sequence)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reply.PutFH();
|
||||
reply.Lock(linfo);
|
||||
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
break;
|
||||
} while (true);
|
||||
locker.Unlock();
|
||||
|
||||
linfo = linfo->fNext;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OpenState::Close()
|
||||
{
|
||||
if (!fOpened)
|
||||
return B_OK;
|
||||
|
||||
MutexLocker _(fLock);
|
||||
fOpened = false;
|
||||
|
||||
uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
|
||||
do {
|
||||
RPC::Server* serv = fFileSystem->Server();
|
||||
Request request(serv, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
req.Close(sequence, fStateID, fStateSeq);
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK) {
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
return result;
|
||||
}
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
sequence += IncrementSequence(reply.NFS4Error());
|
||||
|
||||
// RFC 3530 8.10.1. Some servers does not do anything to help client
|
||||
// recognize retried CLOSE requests so we just assume that BAD_STATEID
|
||||
// on CLOSE request is just a result of retransmission.
|
||||
if (reply.NFS4Error() == NFS4ERR_BAD_STATEID) {
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), serv, NULL, this, &sequence))
|
||||
continue;
|
||||
fFileSystem->OpenOwnerSequenceUnlock(sequence);
|
||||
|
||||
reply.PutFH();
|
||||
|
||||
return reply.Close();
|
||||
} while (true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef OPENSTATE_H
|
||||
#define OPENSTATE_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <util/KernelReferenceable.h>
|
||||
|
||||
#include "Cookie.h"
|
||||
#include "NFS4Object.h"
|
||||
|
||||
|
||||
struct OpenState : public NFS4Object, public KernelReferenceable,
|
||||
public DoublyLinkedListLinkImpl<OpenState> {
|
||||
OpenState();
|
||||
~OpenState();
|
||||
|
||||
uint64 fClientID;
|
||||
|
||||
int fMode;
|
||||
mutex fLock;
|
||||
|
||||
uint32 fStateID[3];
|
||||
uint32 fStateSeq;
|
||||
|
||||
bool fOpened;
|
||||
Delegation* fDelegation;
|
||||
|
||||
LockInfo* fLocks;
|
||||
mutex fLocksLock;
|
||||
|
||||
LockOwner* fLockOwners;
|
||||
mutex fOwnerLock;
|
||||
|
||||
LockOwner* GetLockOwner(uint32 owner);
|
||||
|
||||
void AddLock(LockInfo* lock);
|
||||
void RemoveLock(LockInfo* lock, LockInfo* prev);
|
||||
void DeleteLock(LockInfo* lock);
|
||||
|
||||
status_t Reclaim(uint64 newClientID);
|
||||
|
||||
status_t Close();
|
||||
|
||||
private:
|
||||
status_t _ReclaimOpen(uint64 newClientID);
|
||||
status_t _ReclaimLocks(uint64 newClientID);
|
||||
status_t _ReleaseLockOwner(LockOwner* owner);
|
||||
};
|
||||
|
||||
|
||||
#endif // OPENSTATE_H
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCAuth.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
#include "RPCDefs.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
Auth::Auth()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const Auth*
|
||||
Auth::CreateNone()
|
||||
{
|
||||
Auth* auth = new(std::nothrow) Auth;
|
||||
if (auth == NULL)
|
||||
return NULL;
|
||||
|
||||
auth->fStream.AddInt(AUTH_NONE);
|
||||
auth->fStream.AddOpaque(NULL, 0);
|
||||
if (auth->fStream.Error() != B_OK) {
|
||||
delete auth;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
|
||||
const Auth*
|
||||
Auth::CreateSys()
|
||||
{
|
||||
Auth* auth = new(std::nothrow) Auth;
|
||||
if (auth == NULL)
|
||||
return NULL;
|
||||
|
||||
XDR::WriteStream xdr;
|
||||
xdr.AddUInt(time(NULL));
|
||||
|
||||
char hostname[255];
|
||||
if (gethostname(hostname, 255) < 0)
|
||||
strcpy(hostname, "unknown");
|
||||
xdr.AddString(hostname, 255);
|
||||
|
||||
xdr.AddUInt(getuid());
|
||||
xdr.AddUInt(getgid());
|
||||
|
||||
int count = getgroups(0, NULL);
|
||||
gid_t* groups = (gid_t*)malloc(count * sizeof(gid_t));
|
||||
int len = getgroups(count, groups);
|
||||
if (len > 0) {
|
||||
len = min_c(len, 16);
|
||||
xdr.AddUInt(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
xdr.AddUInt((uint32)groups[i]);
|
||||
} else
|
||||
xdr.AddUInt(0);
|
||||
free(groups);
|
||||
if (xdr.Error() != B_OK) {
|
||||
delete auth;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auth->fStream.AddInt(AUTH_SYS);
|
||||
auth->fStream.AddOpaque(xdr);
|
||||
if (auth->fStream.Error() != B_OK) {
|
||||
delete auth;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCAUTH_H
|
||||
#define RPCAUTH_H
|
||||
|
||||
|
||||
#include "XDR.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class Auth {
|
||||
public:
|
||||
inline const XDR::WriteStream& Stream() const;
|
||||
|
||||
static const Auth* CreateNone();
|
||||
static const Auth* CreateSys();
|
||||
|
||||
private:
|
||||
Auth();
|
||||
|
||||
XDR::WriteStream fStream;
|
||||
};
|
||||
|
||||
|
||||
inline const XDR::WriteStream&
|
||||
Auth::Stream() const
|
||||
{
|
||||
return fStream;
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCAUTH_H
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCCall.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
#include "RPCDefs.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
Call::Call()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Call*
|
||||
Call::Create(uint32 proc, const Auth* creds, const Auth* ver)
|
||||
{
|
||||
ASSERT(creds != NULL);
|
||||
ASSERT(ver != NULL);
|
||||
|
||||
Call* call = new(std::nothrow) Call;
|
||||
if (call == NULL)
|
||||
return NULL;
|
||||
|
||||
// XID will be determined and set by RPC::Server
|
||||
call->fXIDPosition = call->fStream.Current();
|
||||
call->fStream.AddUInt(0);
|
||||
|
||||
call->fStream.AddInt(CALL);
|
||||
call->fStream.AddUInt(VERSION);
|
||||
call->fStream.AddUInt(PROGRAM_NFS);
|
||||
call->fStream.AddUInt(NFS_VERSION);
|
||||
call->fStream.AddUInt(proc);
|
||||
|
||||
call->fStream.Append(creds->Stream());
|
||||
delete creds;
|
||||
|
||||
call->fStream.Append(ver->Stream());
|
||||
delete ver;
|
||||
|
||||
if (call->fStream.Error() != B_OK) {
|
||||
delete call;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
|
||||
Call::~Call()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Call::SetXID(uint32 xid)
|
||||
{
|
||||
fStream.InsertUInt(fXIDPosition, xid);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCCALL_H
|
||||
#define RPCCALL_H
|
||||
|
||||
|
||||
#include "RPCAuth.h"
|
||||
#include "XDR.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class Call {
|
||||
public:
|
||||
static Call* Create(uint32 proc, const Auth* creds,
|
||||
const Auth* ver);
|
||||
~Call();
|
||||
|
||||
void SetXID(uint32 x);
|
||||
|
||||
inline XDR::WriteStream& Stream();
|
||||
|
||||
private:
|
||||
Call();
|
||||
|
||||
XDR::Stream::Position fXIDPosition;
|
||||
|
||||
XDR::WriteStream fStream;
|
||||
};
|
||||
|
||||
|
||||
inline XDR::WriteStream&
|
||||
Call::Stream()
|
||||
{
|
||||
return fStream;
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCCALL_H
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCCallback.h"
|
||||
|
||||
#include "RPCCallbackRequest.h"
|
||||
#include "RPCServer.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
Callback::Callback(Server* server)
|
||||
:
|
||||
fServer(server)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Callback::EnqueueRequest(CallbackRequest* request, Connection* connection)
|
||||
{
|
||||
ASSERT(request != NULL);
|
||||
ASSERT(connection != NULL);
|
||||
return fServer->PrivateData()->ProcessCallback(request, connection);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCCALLBACK_H
|
||||
#define RPCCALLBACK_H
|
||||
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class CallbackServer;
|
||||
class CallbackRequest;
|
||||
class Server;
|
||||
|
||||
class Callback {
|
||||
public:
|
||||
Callback(Server* server);
|
||||
|
||||
inline void SetID(int32 id);
|
||||
inline int32 ID();
|
||||
|
||||
inline void SetCBServer(CallbackServer* server);
|
||||
inline CallbackServer* CBServer();
|
||||
|
||||
status_t EnqueueRequest(CallbackRequest* request,
|
||||
Connection* connection);
|
||||
|
||||
private:
|
||||
CallbackServer* fCBServer;
|
||||
Server* fServer;
|
||||
int32 fID;
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
Callback::SetID(int32 id)
|
||||
{
|
||||
fID = id;
|
||||
}
|
||||
|
||||
|
||||
inline int32
|
||||
Callback::ID()
|
||||
{
|
||||
return fID;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
Callback::SetCBServer(CallbackServer* server)
|
||||
{
|
||||
fCBServer = server;
|
||||
}
|
||||
|
||||
|
||||
inline CallbackServer*
|
||||
Callback::CBServer()
|
||||
{
|
||||
return fCBServer;
|
||||
}
|
||||
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCCALLBACK_H
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCCallbackReply.h"
|
||||
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
#include "RPCDefs.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
CallbackReply::CallbackReply()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CallbackReply*
|
||||
CallbackReply::Create(uint32 xid, AcceptStat rpcError)
|
||||
{
|
||||
CallbackReply* reply = new(std::nothrow) CallbackReply;
|
||||
if (reply == NULL)
|
||||
return NULL;
|
||||
|
||||
reply->fStream.AddUInt(xid);
|
||||
|
||||
reply->fStream.AddInt(REPLY);
|
||||
reply->fStream.AddUInt(MSG_ACCEPTED);
|
||||
|
||||
reply->fStream.AddInt(AUTH_NONE);
|
||||
reply->fStream.AddOpaque(NULL, 0);
|
||||
|
||||
reply->fStream.AddUInt(rpcError);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
CallbackReply::~CallbackReply()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCCALLBACKREPLY_H
|
||||
#define RPCCALLBACKREPLY_H
|
||||
|
||||
|
||||
#include "RPCDefs.h"
|
||||
#include "XDR.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class CallbackReply {
|
||||
public:
|
||||
static CallbackReply* Create(uint32 xid,
|
||||
AcceptStat rpcError = SUCCESS);
|
||||
~CallbackReply();
|
||||
|
||||
inline XDR::WriteStream& Stream();
|
||||
|
||||
private:
|
||||
CallbackReply();
|
||||
|
||||
XDR::WriteStream fStream;
|
||||
};
|
||||
|
||||
|
||||
inline XDR::WriteStream&
|
||||
CallbackReply::Stream()
|
||||
{
|
||||
return fStream;
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCCALLBACKREPLY_H
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCCallbackRequest.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
#include "NFS4Defs.h"
|
||||
#include "RPCDefs.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
CallbackRequest::CallbackRequest(void* buffer, int size)
|
||||
:
|
||||
fError(B_BAD_VALUE),
|
||||
fRPCError(GARBAGE_ARGS),
|
||||
fStream(buffer, size),
|
||||
fBuffer(buffer)
|
||||
{
|
||||
ASSERT(buffer != NULL);
|
||||
|
||||
fXID = fStream.GetUInt();
|
||||
|
||||
if (fStream.GetUInt() != CALL)
|
||||
return;
|
||||
|
||||
if (fStream.GetUInt() != VERSION)
|
||||
return;
|
||||
|
||||
if (fStream.GetUInt() != PROGRAM_NFS_CB) {
|
||||
fRPCError = PROG_UNAVAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fStream.GetUInt() != NFS_CB_VERSION) {
|
||||
fRPCError = PROG_MISMATCH;
|
||||
return;
|
||||
}
|
||||
|
||||
fProcedure = fStream.GetUInt();
|
||||
|
||||
fStream.GetUInt();
|
||||
fStream.GetOpaque(NULL);
|
||||
|
||||
fStream.GetUInt();
|
||||
fStream.GetOpaque(NULL);
|
||||
|
||||
if (fProcedure == CallbackProcCompound) {
|
||||
fStream.GetOpaque(NULL); // TODO: tag may be important
|
||||
if (fStream.GetUInt() != 0)
|
||||
return;
|
||||
|
||||
fID = fStream.GetUInt();
|
||||
|
||||
fRPCError = SUCCESS;
|
||||
fError = B_OK;
|
||||
} else if (fProcedure == CallbackProcNull) {
|
||||
fRPCError = SUCCESS;
|
||||
fError = B_OK;
|
||||
} else
|
||||
fRPCError = PROC_UNAVAIL;
|
||||
}
|
||||
|
||||
|
||||
CallbackRequest::~CallbackRequest()
|
||||
{
|
||||
free(fBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCCALLBACKREQUEST_H
|
||||
#define RPCCALLBACKREQUEST_H
|
||||
|
||||
|
||||
#include "RPCDefs.h"
|
||||
#include "XDR.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class CallbackRequest {
|
||||
public:
|
||||
CallbackRequest(void* buffer, int size);
|
||||
~CallbackRequest();
|
||||
|
||||
inline uint32 XID();
|
||||
inline uint32 ID();
|
||||
|
||||
inline uint32 Procedure();
|
||||
|
||||
inline status_t Error();
|
||||
inline AcceptStat RPCError();
|
||||
|
||||
inline XDR::ReadStream& Stream();
|
||||
|
||||
private:
|
||||
uint32 fXID;
|
||||
uint32 fID;
|
||||
|
||||
uint32 fProcedure;
|
||||
|
||||
status_t fError;
|
||||
AcceptStat fRPCError;
|
||||
|
||||
XDR::ReadStream fStream;
|
||||
void* fBuffer;
|
||||
};
|
||||
|
||||
|
||||
inline uint32
|
||||
CallbackRequest::XID()
|
||||
{
|
||||
return fXID;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
CallbackRequest::ID()
|
||||
{
|
||||
return fID;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
CallbackRequest::Procedure()
|
||||
{
|
||||
return fProcedure;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
CallbackRequest::Error()
|
||||
{
|
||||
return fError;
|
||||
}
|
||||
|
||||
|
||||
inline AcceptStat
|
||||
CallbackRequest::RPCError()
|
||||
{
|
||||
return fRPCError;
|
||||
}
|
||||
|
||||
|
||||
inline XDR::ReadStream&
|
||||
CallbackRequest::Stream()
|
||||
{
|
||||
return fStream;
|
||||
}
|
||||
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCCALLBACKREQUEST_H
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright 2012-2013 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCCallbackServer.h"
|
||||
|
||||
#include "NFS4Defs.h"
|
||||
#include "RPCCallback.h"
|
||||
#include "RPCCallbackReply.h"
|
||||
#include "RPCCallbackRequest.h"
|
||||
#include "RPCServer.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
CallbackServer* gRPCCallbackServer = NULL;
|
||||
CallbackServer* gRPCCallbackServer6 = NULL;
|
||||
|
||||
|
||||
CallbackServer::CallbackServer(int networkFamily)
|
||||
:
|
||||
fConnectionList(NULL),
|
||||
fListener(NULL),
|
||||
fThreadRunning(false),
|
||||
fCallbackArray(NULL),
|
||||
fArraySize(0),
|
||||
fFreeSlot(-1),
|
||||
fNetworkFamily(networkFamily)
|
||||
{
|
||||
mutex_init(&fConnectionLock, NULL);
|
||||
mutex_init(&fThreadLock, NULL);
|
||||
rw_lock_init(&fArrayLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
CallbackServer::~CallbackServer()
|
||||
{
|
||||
StopServer();
|
||||
|
||||
free(fCallbackArray);
|
||||
rw_lock_destroy(&fArrayLock);
|
||||
mutex_destroy(&fThreadLock);
|
||||
mutex_destroy(&fConnectionLock);
|
||||
}
|
||||
|
||||
|
||||
CallbackServer*
|
||||
CallbackServer::Get(Server* server)
|
||||
{
|
||||
ASSERT(server != NULL);
|
||||
|
||||
int family = server->ID().Family();
|
||||
ASSERT(family == AF_INET || family == AF_INET6);
|
||||
|
||||
int idx;
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
idx = 0;
|
||||
break;
|
||||
case AF_INET6:
|
||||
idx = 1;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MutexLocker _(fServerCreationLock);
|
||||
if (fServers[idx] == NULL)
|
||||
fServers[idx] = new CallbackServer(family);
|
||||
return fServers[idx];
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CallbackServer::ShutdownAll()
|
||||
{
|
||||
MutexLocker _(fServerCreationLock);
|
||||
for (unsigned int i = 0; i < sizeof(fServers) / sizeof(fServers[0]); i++)
|
||||
delete fServers[i];
|
||||
memset(&fServers, 0, sizeof(fServers));
|
||||
}
|
||||
|
||||
|
||||
mutex CallbackServer::fServerCreationLock = MUTEX_INITIALIZER(NULL);
|
||||
CallbackServer* CallbackServer::fServers[2] = { NULL, NULL };
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::RegisterCallback(Callback* callback)
|
||||
{
|
||||
ASSERT(callback != NULL);
|
||||
|
||||
status_t result = StartServer();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
WriteLocker _(fArrayLock);
|
||||
if (fFreeSlot == -1) {
|
||||
uint32 newSize = max_c(fArraySize * 2, 4);
|
||||
uint32 size = newSize * sizeof(CallbackSlot);
|
||||
CallbackSlot* array = reinterpret_cast<CallbackSlot*>(malloc(size));
|
||||
if (array == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
if (fCallbackArray != NULL)
|
||||
memcpy(array, fCallbackArray, fArraySize * sizeof(CallbackSlot));
|
||||
|
||||
for (uint32 i = fArraySize; i < newSize; i++)
|
||||
array[i].fNext = i + 1;
|
||||
|
||||
array[newSize - 1].fNext = -1;
|
||||
|
||||
fCallbackArray = array;
|
||||
fFreeSlot = fArraySize;
|
||||
fArraySize = newSize;
|
||||
}
|
||||
|
||||
int32 id = fFreeSlot;
|
||||
fFreeSlot = fCallbackArray[id].fNext;
|
||||
|
||||
fCallbackArray[id].fCallback = callback;
|
||||
callback->SetID(id);
|
||||
callback->SetCBServer(this);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::UnregisterCallback(Callback* callback)
|
||||
{
|
||||
ASSERT(callback != NULL);
|
||||
ASSERT(callback->CBServer() == this);
|
||||
|
||||
int32 id = callback->ID();
|
||||
|
||||
WriteLocker _(fArrayLock);
|
||||
fCallbackArray[id].fNext = fFreeSlot;
|
||||
fFreeSlot = id;
|
||||
|
||||
callback->SetCBServer(NULL);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::StartServer()
|
||||
{
|
||||
MutexLocker _(fThreadLock);
|
||||
if (fThreadRunning)
|
||||
return B_OK;
|
||||
|
||||
status_t result = ConnectionListener::Listen(&fListener, fNetworkFamily);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fThread = spawn_kernel_thread(&CallbackServer::ListenerThreadLauncher,
|
||||
"NFSv4 Callback Listener", B_NORMAL_PRIORITY, this);
|
||||
if (fThread < B_OK)
|
||||
return fThread;
|
||||
|
||||
fThreadRunning = true;
|
||||
|
||||
result = resume_thread(fThread);
|
||||
if (result != B_OK) {
|
||||
kill_thread(fThread);
|
||||
fThreadRunning = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::StopServer()
|
||||
{
|
||||
MutexLocker _(&fThreadLock);
|
||||
if (!fThreadRunning)
|
||||
return B_OK;
|
||||
|
||||
fListener->Disconnect();
|
||||
status_t result;
|
||||
wait_for_thread(fThread, &result);
|
||||
|
||||
MutexLocker locker(fConnectionLock);
|
||||
while (fConnectionList != NULL) {
|
||||
ConnectionEntry* entry = fConnectionList;
|
||||
fConnectionList = entry->fNext;
|
||||
entry->fConnection->Disconnect();
|
||||
|
||||
status_t result;
|
||||
wait_for_thread(entry->fThread, &result);
|
||||
|
||||
delete entry->fConnection;
|
||||
delete entry;
|
||||
}
|
||||
|
||||
delete fListener;
|
||||
|
||||
fThreadRunning = false;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::NewConnection(Connection* connection)
|
||||
{
|
||||
ASSERT(connection != NULL);
|
||||
|
||||
ConnectionEntry* entry = new ConnectionEntry;
|
||||
entry->fConnection = connection;
|
||||
entry->fPrev = NULL;
|
||||
|
||||
MutexLocker locker(fConnectionLock);
|
||||
entry->fNext = fConnectionList;
|
||||
if (fConnectionList != NULL)
|
||||
fConnectionList->fPrev = entry;
|
||||
fConnectionList = entry;
|
||||
locker.Unlock();
|
||||
|
||||
void** arguments = reinterpret_cast<void**>(malloc(sizeof(void*) * 2));
|
||||
if (arguments == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
arguments[0] = this;
|
||||
arguments[1] = entry;
|
||||
|
||||
thread_id thread;
|
||||
thread = spawn_kernel_thread(&CallbackServer::ConnectionThreadLauncher,
|
||||
"NFSv4 Callback Connection", B_NORMAL_PRIORITY, arguments);
|
||||
if (thread < B_OK) {
|
||||
ReleaseConnection(entry);
|
||||
free(arguments);
|
||||
return thread;
|
||||
}
|
||||
|
||||
entry->fThread = thread;
|
||||
|
||||
status_t result = resume_thread(thread);
|
||||
if (result != B_OK) {
|
||||
kill_thread(thread);
|
||||
ReleaseConnection(entry);
|
||||
free(arguments);
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::ReleaseConnection(ConnectionEntry* entry)
|
||||
{
|
||||
ASSERT(entry != NULL);
|
||||
|
||||
MutexLocker _(fConnectionLock);
|
||||
if (entry->fNext != NULL)
|
||||
entry->fNext->fPrev = entry->fPrev;
|
||||
if (entry->fPrev != NULL)
|
||||
entry->fPrev->fNext = entry->fNext;
|
||||
else
|
||||
fConnectionList = entry->fNext;
|
||||
|
||||
delete entry->fConnection;
|
||||
delete entry;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::ConnectionThreadLauncher(void* object)
|
||||
{
|
||||
ASSERT(object != NULL);
|
||||
|
||||
void** objects = reinterpret_cast<void**>(object);
|
||||
CallbackServer* server = reinterpret_cast<CallbackServer*>(objects[0]);
|
||||
ConnectionEntry* entry = reinterpret_cast<ConnectionEntry*>(objects[1]);
|
||||
free(objects);
|
||||
|
||||
return server->ConnectionThread(entry);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::ConnectionThread(ConnectionEntry* entry)
|
||||
{
|
||||
ASSERT(entry != NULL);
|
||||
|
||||
Connection* connection = entry->fConnection;
|
||||
CallbackReply* reply;
|
||||
|
||||
while (fThreadRunning) {
|
||||
uint32 size;
|
||||
void* buffer;
|
||||
status_t result = connection->Receive(&buffer, &size);
|
||||
if (result != B_OK) {
|
||||
if (result != ECONNABORTED)
|
||||
ReleaseConnection(entry);
|
||||
return result;
|
||||
}
|
||||
|
||||
CallbackRequest* request = new CallbackRequest(buffer, size);
|
||||
if (request == NULL || request->Error() != B_OK) {
|
||||
free(buffer);
|
||||
continue;
|
||||
} else if (request != NULL && request->Error() != B_OK) {
|
||||
reply = CallbackReply::Create(request->XID(), request->RPCError());
|
||||
if (reply != NULL) {
|
||||
connection->Send(reply->Stream().Buffer(),
|
||||
reply->Stream().Size());
|
||||
delete reply;
|
||||
}
|
||||
free(buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (request->Procedure()) {
|
||||
case CallbackProcCompound:
|
||||
GetCallback(request->ID())->EnqueueRequest(request, connection);
|
||||
break;
|
||||
|
||||
case CallbackProcNull:
|
||||
reply = CallbackReply::Create(request->XID());
|
||||
if (reply != NULL) {
|
||||
connection->Send(reply->Stream().Buffer(),
|
||||
reply->Stream().Size());
|
||||
delete reply;
|
||||
}
|
||||
|
||||
default:
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::ListenerThreadLauncher(void* object)
|
||||
{
|
||||
ASSERT(object != NULL);
|
||||
|
||||
CallbackServer* server = reinterpret_cast<CallbackServer*>(object);
|
||||
return server->ListenerThread();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CallbackServer::ListenerThread()
|
||||
{
|
||||
while (fThreadRunning) {
|
||||
Connection* connection;
|
||||
|
||||
status_t result = fListener->AcceptConnection(&connection);
|
||||
if (result != B_OK) {
|
||||
fThreadRunning = false;
|
||||
return result;
|
||||
}
|
||||
result = NewConnection(connection);
|
||||
if (result != B_OK)
|
||||
delete connection;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2012-2013 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCCALLBACKSERVER_H
|
||||
#define RPCCALLBACKSERVER_H
|
||||
|
||||
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class Callback;
|
||||
class Server;
|
||||
|
||||
struct ConnectionEntry {
|
||||
Connection* fConnection;
|
||||
thread_id fThread;
|
||||
|
||||
ConnectionEntry* fNext;
|
||||
ConnectionEntry* fPrev;
|
||||
};
|
||||
|
||||
union CallbackSlot {
|
||||
Callback* fCallback;
|
||||
int32 fNext;
|
||||
};
|
||||
|
||||
class CallbackServer {
|
||||
public:
|
||||
CallbackServer(int networkFamily);
|
||||
~CallbackServer();
|
||||
|
||||
static CallbackServer* Get(Server* server);
|
||||
static void ShutdownAll();
|
||||
|
||||
status_t RegisterCallback(Callback* callback);
|
||||
status_t UnregisterCallback(Callback* callback);
|
||||
|
||||
inline PeerAddress LocalID();
|
||||
|
||||
protected:
|
||||
status_t StartServer();
|
||||
status_t StopServer();
|
||||
|
||||
status_t NewConnection(Connection* connection);
|
||||
status_t ReleaseConnection(ConnectionEntry* entry);
|
||||
|
||||
static status_t ListenerThreadLauncher(void* object);
|
||||
status_t ListenerThread();
|
||||
|
||||
static status_t ConnectionThreadLauncher(void* object);
|
||||
status_t ConnectionThread(ConnectionEntry* entry);
|
||||
|
||||
inline Callback* GetCallback(int32 id);
|
||||
|
||||
private:
|
||||
static mutex fServerCreationLock;
|
||||
static CallbackServer* fServers[2];
|
||||
|
||||
mutex fConnectionLock;
|
||||
ConnectionEntry* fConnectionList;
|
||||
ConnectionListener* fListener;
|
||||
|
||||
mutex fThreadLock;
|
||||
thread_id fThread;
|
||||
bool fThreadRunning;
|
||||
|
||||
rw_lock fArrayLock;
|
||||
CallbackSlot* fCallbackArray;
|
||||
uint32 fArraySize;
|
||||
int32 fFreeSlot;
|
||||
|
||||
int fNetworkFamily;
|
||||
};
|
||||
|
||||
|
||||
inline PeerAddress
|
||||
CallbackServer::LocalID()
|
||||
{
|
||||
PeerAddress address;
|
||||
|
||||
ASSERT(fListener != NULL);
|
||||
fListener->GetLocalAddress(&address);
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
inline Callback*
|
||||
CallbackServer::GetCallback(int32 id)
|
||||
{
|
||||
ReadLocker _(fArrayLock);
|
||||
if (id >= 0 && static_cast<uint32>(id) < fArraySize)
|
||||
return fCallbackArray[id].fCallback;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
#endif // RPCCALLBACKSERVER_H
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCDEFS_H
|
||||
#define RPCDEFS_H
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
enum {
|
||||
VERSION = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
PROGRAM_NFS = 100003,
|
||||
PROGRAM_NFS_CB = 0x40000000
|
||||
};
|
||||
|
||||
enum {
|
||||
NFS_VERSION = 4,
|
||||
NFS_CB_VERSION = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
CALL = 0,
|
||||
REPLY = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
MSG_ACCEPTED = 0,
|
||||
MSG_DENIED = 1
|
||||
};
|
||||
|
||||
enum AcceptStat {
|
||||
SUCCESS = 0, /* RPC executed successfully */
|
||||
PROG_UNAVAIL = 1, /* remote hasn't exported program */
|
||||
PROG_MISMATCH = 2, /* remote can't support version # */
|
||||
PROC_UNAVAIL = 3, /* program can't support procedure */
|
||||
GARBAGE_ARGS = 4, /* procedure can't decode params */
|
||||
SYSTEM_ERR = 5 /* e.g. memory allocation failure */
|
||||
};
|
||||
|
||||
enum RejectStat {
|
||||
RPC_MISMATCH = 0, /* RPC version number != 2 */
|
||||
AUTH_ERROR = 1 /* remote can't authenticate caller */
|
||||
};
|
||||
|
||||
enum AuthFlavour {
|
||||
AUTH_NONE = 0,
|
||||
AUTH_SYS = 1
|
||||
};
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCDEFS_H
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCReply.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
#include "RPCDefs.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
Reply::Reply(void* buffer, int size)
|
||||
:
|
||||
fError(B_OK),
|
||||
fStream(buffer, size),
|
||||
fBuffer(buffer)
|
||||
{
|
||||
ASSERT(buffer != NULL);
|
||||
|
||||
fXID = fStream.GetUInt();
|
||||
if (fStream.GetInt() != REPLY) {
|
||||
fError = B_BAD_VALUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fStream.GetInt() == MSG_ACCEPTED) {
|
||||
fStream.GetInt();
|
||||
fStream.GetOpaque(NULL);
|
||||
|
||||
switch (fStream.GetInt()) {
|
||||
case SUCCESS:
|
||||
return;
|
||||
case PROG_UNAVAIL:
|
||||
case PROG_MISMATCH:
|
||||
case PROC_UNAVAIL:
|
||||
fError = B_DEVICE_NOT_FOUND;
|
||||
return;
|
||||
case GARBAGE_ARGS:
|
||||
fError = B_MISMATCHED_VALUES;
|
||||
return;
|
||||
case SYSTEM_ERR:
|
||||
fError = B_ERROR;
|
||||
return;
|
||||
default:
|
||||
fError = B_BAD_VALUE;
|
||||
return;
|
||||
}
|
||||
} else { // MSG_DENIED
|
||||
if (fStream.GetInt() == RPC_MISMATCH) {
|
||||
fError = B_DEVICE_NOT_FOUND;
|
||||
return;
|
||||
} else { // AUTH_ERROR
|
||||
fError = B_PERMISSION_DENIED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reply::~Reply()
|
||||
{
|
||||
free(fBuffer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCREPLY_H
|
||||
#define RPCREPLY_H
|
||||
|
||||
|
||||
#include "XDR.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class Reply {
|
||||
public:
|
||||
Reply(void* buffer, int size);
|
||||
~Reply();
|
||||
|
||||
inline uint32 GetXID();
|
||||
|
||||
inline status_t Error();
|
||||
inline XDR::ReadStream& Stream();
|
||||
|
||||
private:
|
||||
uint32 fXID;
|
||||
|
||||
status_t fError;
|
||||
|
||||
XDR::ReadStream fStream;
|
||||
void* fBuffer;
|
||||
};
|
||||
|
||||
inline uint32
|
||||
Reply::GetXID()
|
||||
{
|
||||
return fXID;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
Reply::Error()
|
||||
{
|
||||
return fError;
|
||||
}
|
||||
|
||||
|
||||
inline XDR::ReadStream&
|
||||
Reply::Stream()
|
||||
{
|
||||
return fStream;
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCREPLY_H
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RPCServer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "RPCCallbackServer.h"
|
||||
#include "RPCReply.h"
|
||||
|
||||
|
||||
using namespace RPC;
|
||||
|
||||
|
||||
RequestManager::RequestManager()
|
||||
:
|
||||
fQueueHead(NULL),
|
||||
fQueueTail(NULL)
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
RequestManager::~RequestManager()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RequestManager::AddRequest(Request* request)
|
||||
{
|
||||
ASSERT(request != NULL);
|
||||
|
||||
MutexLocker _(fLock);
|
||||
if (fQueueTail != NULL)
|
||||
fQueueTail->fNext = request;
|
||||
else
|
||||
fQueueHead = request;
|
||||
fQueueTail = request;
|
||||
request->fNext = NULL;
|
||||
}
|
||||
|
||||
|
||||
Request*
|
||||
RequestManager::FindRequest(uint32 xid)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
Request* req = fQueueHead;
|
||||
Request* prev = NULL;
|
||||
while (req != NULL) {
|
||||
if (req->fXID == xid) {
|
||||
if (prev != NULL)
|
||||
prev->fNext = req->fNext;
|
||||
if (fQueueTail == req)
|
||||
fQueueTail = prev;
|
||||
if (fQueueHead == req)
|
||||
fQueueHead = req->fNext;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
prev = req;
|
||||
req = req->fNext;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Server::Server(Connection* connection, PeerAddress* address)
|
||||
:
|
||||
fConnection(connection),
|
||||
fAddress(address),
|
||||
fPrivateData(NULL),
|
||||
fCallback(NULL),
|
||||
fRepairCount(0),
|
||||
fXID(rand() << 1)
|
||||
{
|
||||
ASSERT(connection != NULL);
|
||||
ASSERT(address != NULL);
|
||||
|
||||
mutex_init(&fCallbackLock, NULL);
|
||||
mutex_init(&fRepairLock, NULL);
|
||||
|
||||
_StartListening();
|
||||
}
|
||||
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
if (fCallback != NULL)
|
||||
fCallback->CBServer()->UnregisterCallback(fCallback);
|
||||
delete fCallback;
|
||||
mutex_destroy(&fCallbackLock);
|
||||
mutex_destroy(&fRepairLock);
|
||||
|
||||
delete fPrivateData;
|
||||
|
||||
fThreadCancel = true;
|
||||
fConnection->Disconnect();
|
||||
|
||||
status_t result;
|
||||
wait_for_thread(fThread, &result);
|
||||
|
||||
delete fConnection;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Server::_StartListening()
|
||||
{
|
||||
fThreadCancel = false;
|
||||
fThreadError = B_OK;
|
||||
fThread = spawn_kernel_thread(&Server::_ListenerThreadStart,
|
||||
"NFSv4 Listener", B_NORMAL_PRIORITY, this);
|
||||
if (fThread < B_OK)
|
||||
return fThread;
|
||||
|
||||
status_t result = resume_thread(fThread);
|
||||
if (result != B_OK) {
|
||||
kill_thread(fThread);
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Server::SendCallAsync(Call* call, Reply** reply, Request** request)
|
||||
{
|
||||
ASSERT(call != NULL);
|
||||
ASSERT(reply != NULL);
|
||||
ASSERT(request != NULL);
|
||||
|
||||
if (fThreadError != B_OK && Repair() != B_OK)
|
||||
return fThreadError;
|
||||
|
||||
Request* req = new(std::nothrow) Request;
|
||||
if (req == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint32 xid = _GetXID();
|
||||
call->SetXID(xid);
|
||||
req->fXID = xid;
|
||||
req->fReply = reply;
|
||||
req->fEvent.Init(&req->fEvent, NULL);
|
||||
req->fDone = false;
|
||||
req->fError = B_OK;
|
||||
req->fNext = NULL;
|
||||
|
||||
fRequests.AddRequest(req);
|
||||
|
||||
*request = req;
|
||||
return ResendCallAsync(call, req);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Server::ResendCallAsync(Call* call, Request* request)
|
||||
{
|
||||
ASSERT(call != NULL);
|
||||
ASSERT(request != NULL);
|
||||
|
||||
if (fThreadError != B_OK && Repair() != B_OK) {
|
||||
fRequests.FindRequest(request->fXID);
|
||||
delete request;
|
||||
return fThreadError;
|
||||
}
|
||||
|
||||
XDR::WriteStream& stream = call->Stream();
|
||||
status_t result = fConnection->Send(stream.Buffer(), stream.Size());
|
||||
if (result != B_OK) {
|
||||
fRequests.FindRequest(request->fXID);
|
||||
delete request;
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Server::WakeCall(Request* request)
|
||||
{
|
||||
ASSERT(request != NULL);
|
||||
|
||||
Request* req = fRequests.FindRequest(request->fXID);
|
||||
if (req == NULL)
|
||||
return B_OK;
|
||||
|
||||
request->fError = B_FILE_ERROR;
|
||||
*request->fReply = NULL;
|
||||
request->fDone = true;
|
||||
request->fEvent.NotifyAll();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Server::Repair()
|
||||
{
|
||||
uint32 thisRepair = fRepairCount;
|
||||
|
||||
MutexLocker _(fRepairLock);
|
||||
if (fRepairCount != thisRepair)
|
||||
return B_OK;
|
||||
|
||||
fThreadCancel = true;
|
||||
|
||||
status_t result = fConnection->Reconnect();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
wait_for_thread(fThread, &result);
|
||||
result = _StartListening();
|
||||
|
||||
if (result == B_OK)
|
||||
fRepairCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Callback*
|
||||
Server::GetCallback()
|
||||
{
|
||||
MutexLocker _(fCallbackLock);
|
||||
|
||||
if (fCallback == NULL) {
|
||||
fCallback = new(std::nothrow) Callback(this);
|
||||
if (fCallback == NULL)
|
||||
return NULL;
|
||||
|
||||
CallbackServer* server = CallbackServer::Get(this);
|
||||
if (server == NULL) {
|
||||
delete fCallback;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (server->RegisterCallback(fCallback) != B_OK) {
|
||||
delete fCallback;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return fCallback;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
Server::_GetXID()
|
||||
{
|
||||
return static_cast<uint32>(atomic_add(&fXID, 1));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Server::_Listener()
|
||||
{
|
||||
status_t result;
|
||||
uint32 size;
|
||||
void* buffer = NULL;
|
||||
|
||||
while (!fThreadCancel) {
|
||||
result = fConnection->Receive(&buffer, &size);
|
||||
if (result == B_NO_MEMORY)
|
||||
continue;
|
||||
else if (result != B_OK) {
|
||||
fThreadError = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
ASSERT(buffer != NULL && size > 0);
|
||||
Reply* reply = new(std::nothrow) Reply(buffer, size);
|
||||
if (reply == NULL) {
|
||||
free(buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
Request* req = fRequests.FindRequest(reply->GetXID());
|
||||
if (req != NULL) {
|
||||
*req->fReply = reply;
|
||||
req->fDone = true;
|
||||
req->fEvent.NotifyAll();
|
||||
} else
|
||||
delete reply;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Server::_ListenerThreadStart(void* object)
|
||||
{
|
||||
ASSERT(object != NULL);
|
||||
|
||||
Server* server = reinterpret_cast<Server*>(object);
|
||||
return server->_Listener();
|
||||
}
|
||||
|
||||
|
||||
ServerManager::ServerManager()
|
||||
:
|
||||
fRoot(NULL)
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
ServerManager::~ServerManager()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ServerManager::Acquire(Server** _server, AddressResolver* resolver,
|
||||
ProgramData* (*createPrivateData)(Server*))
|
||||
{
|
||||
PeerAddress address;
|
||||
status_t result;
|
||||
|
||||
while ((result = resolver->GetNextAddress(&address)) == B_OK) {
|
||||
result = _Acquire(_server, address, createPrivateData);
|
||||
if (result == B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ServerManager::_Acquire(Server** _server, const PeerAddress& address,
|
||||
ProgramData* (*createPrivateData)(Server*))
|
||||
{
|
||||
ASSERT(_server != NULL);
|
||||
ASSERT(createPrivateData != NULL);
|
||||
|
||||
status_t result;
|
||||
|
||||
MutexLocker locker(fLock);
|
||||
ServerNode* node = _Find(address);
|
||||
if (node != NULL) {
|
||||
node->fRefCount++;
|
||||
*_server = node->fServer;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
node = new(std::nothrow) ServerNode;
|
||||
if (node == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
node->fID = address;
|
||||
|
||||
Connection* conn;
|
||||
result = Connection::Connect(&conn, address);
|
||||
if (result != B_OK) {
|
||||
delete node;
|
||||
return result;
|
||||
}
|
||||
|
||||
node->fServer = new Server(conn, &node->fID);
|
||||
if (node->fServer == NULL) {
|
||||
delete node;
|
||||
delete conn;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
node->fServer->SetPrivateData(createPrivateData(node->fServer));
|
||||
|
||||
node->fRefCount = 1;
|
||||
node->fLeft = node->fRight = NULL;
|
||||
|
||||
ServerNode* nd = _Insert(node);
|
||||
if (nd != node) {
|
||||
nd->fRefCount++;
|
||||
|
||||
delete node->fServer;
|
||||
delete node;
|
||||
*_server = nd->fServer;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
*_server = node->fServer;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ServerManager::Release(Server* server)
|
||||
{
|
||||
ASSERT(server != NULL);
|
||||
|
||||
MutexLocker _(fLock);
|
||||
ServerNode* node = _Find(server->ID());
|
||||
if (node != NULL) {
|
||||
node->fRefCount--;
|
||||
|
||||
if (node->fRefCount == 0) {
|
||||
_Delete(node);
|
||||
delete node->fServer;
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ServerNode*
|
||||
ServerManager::_Find(const PeerAddress& address)
|
||||
{
|
||||
ServerNode* node = fRoot;
|
||||
while (node != NULL) {
|
||||
if (node->fID == address)
|
||||
return node;
|
||||
if (node->fID < address)
|
||||
node = node->fRight;
|
||||
else
|
||||
node = node->fLeft;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ServerManager::_Delete(ServerNode* node)
|
||||
{
|
||||
ASSERT(node != NULL);
|
||||
|
||||
bool found = false;
|
||||
ServerNode* previous = NULL;
|
||||
ServerNode* current = fRoot;
|
||||
while (current != NULL) {
|
||||
if (current->fID == node->fID) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (current->fID < node->fID) {
|
||||
previous = current;
|
||||
current = current->fRight;
|
||||
} else {
|
||||
previous = current;
|
||||
current = current->fLeft;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
if (previous == NULL)
|
||||
fRoot = NULL;
|
||||
else if (current->fLeft == NULL && current->fRight == NULL) {
|
||||
if (previous->fID < node->fID)
|
||||
previous->fRight = NULL;
|
||||
else
|
||||
previous->fLeft = NULL;
|
||||
} else if (current->fLeft != NULL && current->fRight == NULL) {
|
||||
if (previous->fID < node->fID)
|
||||
previous->fRight = current->fLeft;
|
||||
else
|
||||
previous->fLeft = current->fLeft;
|
||||
} else if (current->fLeft == NULL && current->fRight != NULL) {
|
||||
if (previous->fID < node->fID)
|
||||
previous->fRight = current->fRight;
|
||||
else
|
||||
previous->fLeft = current->fRight;
|
||||
} else {
|
||||
ServerNode* left_prev = current;
|
||||
ServerNode* left = current->fLeft;
|
||||
|
||||
while (left->fLeft != NULL) {
|
||||
left_prev = left;
|
||||
left = left->fLeft;
|
||||
}
|
||||
|
||||
if (previous->fID < node->fID)
|
||||
previous->fRight = left;
|
||||
else
|
||||
previous->fLeft = left;
|
||||
|
||||
|
||||
left_prev->fLeft = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ServerNode*
|
||||
ServerManager::_Insert(ServerNode* node)
|
||||
{
|
||||
ASSERT(node != NULL);
|
||||
|
||||
ServerNode* previous = NULL;
|
||||
ServerNode* current = fRoot;
|
||||
while (current != NULL) {
|
||||
if (current->fID == node->fID)
|
||||
return current;
|
||||
if (current->fID < node->fID) {
|
||||
previous = current;
|
||||
current = current->fRight;
|
||||
} else {
|
||||
previous = current;
|
||||
current = current->fLeft;
|
||||
}
|
||||
}
|
||||
|
||||
if (previous == NULL)
|
||||
fRoot = node;
|
||||
else if (previous->fID < node->fID)
|
||||
previous->fRight = node;
|
||||
else
|
||||
previous->fLeft = node;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef RPCSERVER_H
|
||||
#define RPCSERVER_H
|
||||
|
||||
|
||||
#include <condition_variable.h>
|
||||
#include <lock.h>
|
||||
|
||||
#include "Connection.h"
|
||||
#include "RPCCall.h"
|
||||
#include "RPCCallback.h"
|
||||
#include "RPCReply.h"
|
||||
|
||||
|
||||
namespace RPC {
|
||||
|
||||
struct Request {
|
||||
uint32 fXID;
|
||||
ConditionVariable fEvent;
|
||||
|
||||
bool fDone;
|
||||
Reply** fReply;
|
||||
status_t fError;
|
||||
|
||||
Request* fNext;
|
||||
};
|
||||
|
||||
class RequestManager {
|
||||
public:
|
||||
RequestManager();
|
||||
~RequestManager();
|
||||
|
||||
void AddRequest(Request* request);
|
||||
Request* FindRequest(uint32 xid);
|
||||
|
||||
private:
|
||||
mutex fLock;
|
||||
|
||||
// Neither SinglyLinkedList nor DoublyLinkedList is what we want
|
||||
// here. And DoublyLinkedQueue is not even a queue.
|
||||
Request* fQueueHead;
|
||||
Request* fQueueTail;
|
||||
|
||||
};
|
||||
|
||||
class ProgramData {
|
||||
public:
|
||||
virtual status_t ProcessCallback(CallbackRequest* request,
|
||||
Connection* connection) = 0;
|
||||
|
||||
virtual ~ProgramData() { }
|
||||
};
|
||||
|
||||
class Server {
|
||||
public:
|
||||
Server(Connection* connection,
|
||||
PeerAddress* address);
|
||||
virtual ~Server();
|
||||
|
||||
status_t SendCallAsync(Call* call, Reply** reply,
|
||||
Request** request);
|
||||
status_t ResendCallAsync(Call* call,
|
||||
Request* request);
|
||||
|
||||
inline status_t WaitCall(Request* request,
|
||||
bigtime_t time);
|
||||
inline status_t CancelCall(Request* request);
|
||||
status_t WakeCall(Request* request);
|
||||
|
||||
status_t Repair();
|
||||
|
||||
inline const PeerAddress& ID() const;
|
||||
inline PeerAddress LocalID() const;
|
||||
|
||||
inline ProgramData* PrivateData();
|
||||
inline void SetPrivateData(ProgramData* privateData);
|
||||
|
||||
Callback* GetCallback();
|
||||
|
||||
private:
|
||||
inline uint32 _GetXID();
|
||||
|
||||
status_t _StartListening();
|
||||
|
||||
status_t _Listener();
|
||||
static status_t _ListenerThreadStart(void* object);
|
||||
|
||||
thread_id fThread;
|
||||
bool fThreadCancel;
|
||||
status_t fThreadError;
|
||||
|
||||
RequestManager fRequests;
|
||||
Connection* fConnection;
|
||||
const PeerAddress* fAddress;
|
||||
|
||||
ProgramData* fPrivateData;
|
||||
|
||||
mutex fCallbackLock;
|
||||
Callback* fCallback;
|
||||
|
||||
uint32 fRepairCount;
|
||||
mutex fRepairLock;
|
||||
|
||||
vint32 fXID;
|
||||
};
|
||||
|
||||
|
||||
inline status_t
|
||||
Server::WaitCall(Request* request, bigtime_t time)
|
||||
{
|
||||
if (request->fDone)
|
||||
return B_OK;
|
||||
return request->fEvent.Wait(B_RELATIVE_TIMEOUT, time);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
Server::CancelCall(Request* request)
|
||||
{
|
||||
fRequests.FindRequest(request->fXID);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
inline const PeerAddress&
|
||||
Server::ID() const
|
||||
{
|
||||
return *fAddress;
|
||||
}
|
||||
|
||||
|
||||
inline PeerAddress
|
||||
Server::LocalID() const
|
||||
{
|
||||
PeerAddress addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
fConnection->GetLocalAddress(&addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
inline ProgramData*
|
||||
Server::PrivateData()
|
||||
{
|
||||
return fPrivateData;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
Server::SetPrivateData(ProgramData* privateData)
|
||||
{
|
||||
delete fPrivateData;
|
||||
fPrivateData = privateData;
|
||||
}
|
||||
|
||||
|
||||
struct ServerNode {
|
||||
PeerAddress fID;
|
||||
Server* fServer;
|
||||
int fRefCount;
|
||||
|
||||
ServerNode* fLeft;
|
||||
ServerNode* fRight;
|
||||
};
|
||||
|
||||
class ServerManager {
|
||||
public:
|
||||
ServerManager();
|
||||
~ServerManager();
|
||||
|
||||
status_t Acquire(Server** _server, AddressResolver* resolver,
|
||||
ProgramData* (*createPrivateData)(Server*));
|
||||
void Release(Server* server);
|
||||
|
||||
private:
|
||||
status_t _Acquire(Server** _server, const PeerAddress& address,
|
||||
ProgramData* (*createPrivateData)(Server*));
|
||||
|
||||
ServerNode* _Find(const PeerAddress& address);
|
||||
void _Delete(ServerNode* node);
|
||||
ServerNode* _Insert(ServerNode* node);
|
||||
|
||||
ServerNode* fRoot;
|
||||
mutex fLock;
|
||||
};
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
|
||||
#endif // RPCSERVER_H
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "ReplyBuilder.h"
|
||||
|
||||
#include "NFS4Defs.h"
|
||||
#include "RPCCallbackReply.h"
|
||||
|
||||
|
||||
ReplyBuilder::ReplyBuilder(uint32 xid)
|
||||
:
|
||||
fStatus(B_OK),
|
||||
fOpCount(0),
|
||||
fReply(RPC::CallbackReply::Create(xid))
|
||||
{
|
||||
_InitHeader();
|
||||
}
|
||||
|
||||
|
||||
ReplyBuilder::~ReplyBuilder()
|
||||
{
|
||||
delete fReply;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplyBuilder::_InitHeader()
|
||||
{
|
||||
fStatusPosition = fReply->Stream().Current();
|
||||
fReply->Stream().AddUInt(0);
|
||||
|
||||
fReply->Stream().AddOpaque(NULL, 0);
|
||||
|
||||
fOpCountPosition = fReply->Stream().Current();
|
||||
fReply->Stream().AddUInt(0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
RPC::CallbackReply*
|
||||
ReplyBuilder::Reply()
|
||||
{
|
||||
fReply->Stream().InsertUInt(fStatusPosition, _HaikuErrorToNFS4(fStatus));
|
||||
fReply->Stream().InsertUInt(fOpCountPosition, fOpCount);
|
||||
|
||||
if (fReply == NULL || fReply->Stream().Error() == B_OK)
|
||||
return fReply;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyBuilder::GetAttr(status_t status, int mask, uint64 size, uint64 change)
|
||||
{
|
||||
if (fStatus != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
fReply->Stream().AddUInt(OpCallbackGetAttr);
|
||||
fReply->Stream().AddUInt(_HaikuErrorToNFS4(fStatus));
|
||||
fStatus = status;
|
||||
|
||||
if (status == B_OK) {
|
||||
uint32 bitmap = 0;
|
||||
if ((mask & CallbackAttrChange) != 0)
|
||||
bitmap |= 1 << FATTR4_CHANGE;
|
||||
if ((mask & CallbackAttrSize) != 0)
|
||||
bitmap |= 1 << FATTR4_SIZE;
|
||||
fReply->Stream().AddUInt(1);
|
||||
fReply->Stream().AddUInt(bitmap);
|
||||
|
||||
XDR::WriteStream str;
|
||||
if ((mask & CallbackAttrChange) != 0)
|
||||
str.AddUHyper(change);
|
||||
|
||||
if ((mask & CallbackAttrSize) != 0)
|
||||
str.AddUHyper(size);
|
||||
fReply->Stream().AddOpaque(str);
|
||||
}
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyBuilder::Recall(status_t status)
|
||||
{
|
||||
if (fStatus != B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
fReply->Stream().AddUInt(OpCallbackRecall);
|
||||
fReply->Stream().AddUInt(_HaikuErrorToNFS4(fStatus));
|
||||
fStatus = status;
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
ReplyBuilder::_HaikuErrorToNFS4(status_t error)
|
||||
{
|
||||
switch (error) {
|
||||
case B_OK: return NFS4_OK;
|
||||
case B_FILE_NOT_FOUND: return NFS4ERR_BADHANDLE;
|
||||
case B_NOT_SUPPORTED: return NFS4ERR_OP_ILLEGAL;
|
||||
default: return NFS4ERR_RESOURCE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef REPLYBUILDER_H
|
||||
#define REPLYBUILDER_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "RPCCallbackReply.h"
|
||||
#include "XDR.h"
|
||||
|
||||
|
||||
class ReplyBuilder {
|
||||
public:
|
||||
ReplyBuilder(uint32 xid);
|
||||
~ReplyBuilder();
|
||||
|
||||
RPC::CallbackReply* Reply();
|
||||
|
||||
status_t GetAttr(status_t status, int mask,
|
||||
uint64 size, uint64 change);
|
||||
status_t Recall(status_t status);
|
||||
|
||||
private:
|
||||
void _InitHeader();
|
||||
|
||||
static uint32 _HaikuErrorToNFS4(status_t error);
|
||||
|
||||
status_t fStatus;
|
||||
XDR::Stream::Position fStatusPosition;
|
||||
|
||||
uint32 fOpCount;
|
||||
XDR::Stream::Position fOpCountPosition;
|
||||
|
||||
RPC::CallbackReply* fReply;
|
||||
};
|
||||
|
||||
|
||||
#endif // REPLYBUILDER_H
|
||||
|
||||
@@ -0,0 +1,845 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "ReplyInterpreter.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
#include "Cookie.h"
|
||||
|
||||
|
||||
FSLocation::~FSLocation()
|
||||
{
|
||||
free(const_cast<char*>(fRootPath));
|
||||
for (uint32 i = 0; i < fCount; i++)
|
||||
free(const_cast<char*>(fLocations[i]));
|
||||
delete[] fLocations;
|
||||
}
|
||||
|
||||
|
||||
FSLocations::~FSLocations()
|
||||
{
|
||||
free(const_cast<char*>(fRootPath));
|
||||
delete[] fLocations;
|
||||
}
|
||||
|
||||
|
||||
AttrValue::AttrValue()
|
||||
:
|
||||
fAttribute(0),
|
||||
fFreePointer(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AttrValue::~AttrValue()
|
||||
{
|
||||
if (fFreePointer)
|
||||
free(fData.fPointer);
|
||||
if (fAttribute == FATTR4_FS_LOCATIONS)
|
||||
delete fData.fLocations;
|
||||
}
|
||||
|
||||
|
||||
DirEntry::DirEntry()
|
||||
:
|
||||
fName(NULL),
|
||||
fAttrs(NULL),
|
||||
fAttrCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
DirEntry::~DirEntry()
|
||||
{
|
||||
free(const_cast<char*>(fName));
|
||||
delete[] fAttrs;
|
||||
}
|
||||
|
||||
|
||||
ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply)
|
||||
:
|
||||
fNFS4Error(NFS4_OK),
|
||||
fDecodeError(false),
|
||||
fReply(reply)
|
||||
{
|
||||
if (reply != NULL)
|
||||
_ParseHeader();
|
||||
}
|
||||
|
||||
|
||||
ReplyInterpreter::~ReplyInterpreter()
|
||||
{
|
||||
delete fReply;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplyInterpreter::_ParseHeader()
|
||||
{
|
||||
fNFS4Error = fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetOpaque(NULL);
|
||||
fReply->Stream().GetUInt();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Access(uint32* supported, uint32* allowed)
|
||||
{
|
||||
status_t res = _OperationError(OpAccess);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
uint32 support = fReply->Stream().GetUInt();
|
||||
uint32 allow = fReply->Stream().GetUInt();
|
||||
|
||||
if (supported != NULL)
|
||||
*supported = support;
|
||||
if (allowed != NULL)
|
||||
*allowed = allow;
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Close()
|
||||
{
|
||||
status_t res = _OperationError(OpClose);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Commit()
|
||||
{
|
||||
status_t res = _OperationError(OpCommit);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
fReply->Stream().GetOpaque(NULL);
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Create(uint64* before, uint64* after, bool& atomic)
|
||||
{
|
||||
status_t res = _OperationError(OpCreate);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
atomic = fReply->Stream().GetBoolean();
|
||||
*before = fReply->Stream().GetUHyper();
|
||||
*after = fReply->Stream().GetUHyper();
|
||||
|
||||
uint32 count = fReply->Stream().GetUInt();
|
||||
for (uint32 i = 0; i < count; i++)
|
||||
fReply->Stream().GetUInt();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
// Bit Twiddling Hacks
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html
|
||||
static inline uint32 CountBits(uint32 v)
|
||||
{
|
||||
v = v - ((v >> 1) & 0x55555555);
|
||||
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
|
||||
return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
|
||||
{
|
||||
status_t res = _OperationError(OpGetAttr);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
return _DecodeAttrs(fReply->Stream(), attrs, count);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::GetFH(FileHandle* fh)
|
||||
{
|
||||
status_t res = _OperationError(OpGetFH);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
uint32 size;
|
||||
const void* ptr = fReply->Stream().GetOpaque(&size);
|
||||
if (ptr == NULL || size > NFS4_FHSIZE)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (fh != NULL) {
|
||||
fh->fSize = size;
|
||||
memcpy(fh->fData, ptr, size);
|
||||
}
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Link(uint64* before, uint64* after, bool& atomic)
|
||||
{
|
||||
status_t res = _OperationError(OpLink);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
atomic = fReply->Stream().GetBoolean();
|
||||
*before = fReply->Stream().GetUHyper();
|
||||
*after = fReply->Stream().GetUHyper();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Lock(LockInfo* linfo)
|
||||
{
|
||||
status_t res = _OperationError(OpLock);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
|
||||
linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
|
||||
linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
|
||||
linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::LockT(uint64* pos, uint64* len, LockType* type)
|
||||
{
|
||||
status_t res = _OperationError(OpLockU);
|
||||
if (res != B_WOULD_BLOCK || NFS4Error() != NFS4ERR_DENIED)
|
||||
return res;
|
||||
|
||||
*pos = fReply->Stream().GetUHyper();
|
||||
*len = fReply->Stream().GetUHyper();
|
||||
*type = static_cast<LockType>(fReply->Stream().GetInt());
|
||||
|
||||
fReply->Stream().GetUHyper();
|
||||
fReply->Stream().GetOpaque(NULL);
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::LockU(LockInfo* linfo)
|
||||
{
|
||||
status_t res = _OperationError(OpLockU);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
|
||||
linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
|
||||
linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
|
||||
linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Open(uint32* id, uint32* seq, bool* confirm,
|
||||
OpenDelegationData* delegData, ChangeInfo* changeInfo)
|
||||
{
|
||||
status_t res = _OperationError(OpOpen);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
*seq = fReply->Stream().GetUInt();
|
||||
id[0] = fReply->Stream().GetUInt();
|
||||
id[1] = fReply->Stream().GetUInt();
|
||||
id[2] = fReply->Stream().GetUInt();
|
||||
|
||||
// change info
|
||||
bool atomic = fReply->Stream().GetBoolean();
|
||||
uint64 before = fReply->Stream().GetUHyper();
|
||||
uint64 after = fReply->Stream().GetUHyper();
|
||||
if (changeInfo != NULL) {
|
||||
changeInfo->fAtomic = atomic;
|
||||
changeInfo->fBefore = before;
|
||||
changeInfo->fAfter = after;
|
||||
}
|
||||
|
||||
uint32 flags = fReply->Stream().GetUInt();
|
||||
*confirm = (flags & OPEN4_RESULT_CONFIRM) == OPEN4_RESULT_CONFIRM;
|
||||
|
||||
// attrmask
|
||||
uint32 bcount = fReply->Stream().GetUInt();
|
||||
for (uint32 i = 0; i < bcount; i++)
|
||||
fReply->Stream().GetUInt();
|
||||
|
||||
// delegation info
|
||||
uint32 delegation = fReply->Stream().GetUInt();
|
||||
OpenDelegationData data;
|
||||
if (delegData == NULL)
|
||||
delegData = &data;
|
||||
|
||||
if (delegation == OPEN_DELEGATE_NONE) {
|
||||
delegData->fType = OPEN_DELEGATE_NONE;
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
delegData->fStateSeq = fReply->Stream().GetUInt();
|
||||
delegData->fStateID[0] = fReply->Stream().GetUInt();
|
||||
delegData->fStateID[1] = fReply->Stream().GetUInt();
|
||||
delegData->fStateID[2] = fReply->Stream().GetUInt();
|
||||
|
||||
delegData->fRecall = fReply->Stream().GetBoolean();
|
||||
|
||||
switch (delegation) {
|
||||
case OPEN_DELEGATE_READ:
|
||||
delegData->fType = OPEN_DELEGATE_READ;
|
||||
break;
|
||||
case OPEN_DELEGATE_WRITE:
|
||||
delegData->fType = OPEN_DELEGATE_WRITE;
|
||||
|
||||
int32 limitBy = fReply->Stream().GetInt();
|
||||
if (limitBy == NFS_LIMIT_SIZE)
|
||||
delegData->fSpaceLimit = fReply->Stream().GetUHyper();
|
||||
else if (limitBy == NFS_LIMIT_BLOCKS) {
|
||||
uint32 numBlocks = fReply->Stream().GetUInt();
|
||||
delegData->fSpaceLimit = fReply->Stream().GetUInt() * numBlocks;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ACE data
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetOpaque(NULL);
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::OpenConfirm(uint32* stateSeq)
|
||||
{
|
||||
status_t res = _OperationError(OpOpenConfirm);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
*stateSeq = fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetUInt();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Read(void* buffer, uint32* size, bool* eof)
|
||||
{
|
||||
status_t res = _OperationError(OpRead);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
*eof = fReply->Stream().GetBoolean();
|
||||
const void* ptr = fReply->Stream().GetOpaque(size);
|
||||
memcpy(buffer, ptr, *size);
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
|
||||
DirEntry** dirents, uint32* _count, bool* eof)
|
||||
{
|
||||
status_t res = _OperationError(OpReadDir);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
*cookieVerf = fReply->Stream().GetUHyper();
|
||||
|
||||
bool isNext;
|
||||
uint32 count = 0;
|
||||
|
||||
// TODO: using list instead of array would make this much more elegant
|
||||
// and efficient
|
||||
XDR::Stream::Position dataStart = fReply->Stream().Current();
|
||||
isNext = fReply->Stream().GetBoolean();
|
||||
while (isNext) {
|
||||
fReply->Stream().GetUHyper();
|
||||
|
||||
free(fReply->Stream().GetString());
|
||||
AttrValue* values;
|
||||
uint32 attrCount;
|
||||
_DecodeAttrs(fReply->Stream(), &values, &attrCount);
|
||||
delete[] values;
|
||||
|
||||
count++;
|
||||
|
||||
isNext = fReply->Stream().GetBoolean();
|
||||
}
|
||||
|
||||
DirEntry* entries = new(std::nothrow) DirEntry[count];
|
||||
if (entries == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
count = 0;
|
||||
fReply->Stream().SetPosition(dataStart);
|
||||
isNext = fReply->Stream().GetBoolean();
|
||||
while (isNext) {
|
||||
*cookie = fReply->Stream().GetUHyper();
|
||||
|
||||
entries[count].fName = fReply->Stream().GetString();
|
||||
_DecodeAttrs(fReply->Stream(), &entries[count].fAttrs,
|
||||
&entries[count].fAttrCount);
|
||||
|
||||
count++;
|
||||
|
||||
isNext = fReply->Stream().GetBoolean();
|
||||
}
|
||||
*eof = fReply->Stream().GetBoolean();
|
||||
|
||||
*_count = count;
|
||||
*dirents = entries;
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize)
|
||||
{
|
||||
status_t res = _OperationError(OpReadLink);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
const void* ptr = fReply->Stream().GetOpaque(size);
|
||||
memcpy(buffer, ptr, min_c(*size, maxSize));
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Remove(uint64* before, uint64* after, bool& atomic)
|
||||
{
|
||||
status_t res = _OperationError(OpRemove);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
atomic = fReply->Stream().GetBoolean();
|
||||
*before = fReply->Stream().GetUHyper();
|
||||
*after = fReply->Stream().GetUHyper();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Rename(uint64* fromBefore, uint64* fromAfter,
|
||||
bool& fromAtomic, uint64* toBefore, uint64* toAfter, bool& toAtomic)
|
||||
{
|
||||
status_t res = _OperationError(OpRename);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
fromAtomic = fReply->Stream().GetBoolean();
|
||||
*fromBefore = fReply->Stream().GetUHyper();
|
||||
*fromAfter = fReply->Stream().GetUHyper();
|
||||
|
||||
toAtomic = fReply->Stream().GetBoolean();
|
||||
*toBefore = fReply->Stream().GetUHyper();
|
||||
*toAfter = fReply->Stream().GetUHyper();
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::SetAttr()
|
||||
{
|
||||
status_t res = _OperationError(OpSetAttr);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
uint32 bcount = fReply->Stream().GetUInt();
|
||||
for (uint32 i = 0; i < bcount; i++)
|
||||
fReply->Stream().GetUInt();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
|
||||
{
|
||||
status_t res = _OperationError(OpSetClientID);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
*clientid = fReply->Stream().GetUHyper();
|
||||
*verifier = fReply->Stream().GetUHyper();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::Write(uint32* size)
|
||||
{
|
||||
status_t res = _OperationError(OpWrite);
|
||||
if (res != B_OK)
|
||||
return res;
|
||||
|
||||
*size = fReply->Stream().GetUInt();
|
||||
fReply->Stream().GetInt();
|
||||
fReply->Stream().GetUHyper();
|
||||
|
||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
static const char*
|
||||
sFlattenPathname(XDR::ReadStream& str)
|
||||
{
|
||||
uint32 count = str.GetUInt();
|
||||
char* pathname = NULL;
|
||||
uint32 size = 0;
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
const char* path = str.GetString();
|
||||
size += strlen(path) + 1;
|
||||
if (pathname == NULL) {
|
||||
pathname = reinterpret_cast<char*>(malloc(strlen(path + 1)));
|
||||
pathname[0] = '\0';
|
||||
} else {
|
||||
*pathname++ = '/';
|
||||
pathname = reinterpret_cast<char*>(realloc(pathname, size));
|
||||
}
|
||||
strcat(pathname, path);
|
||||
free(const_cast<char*>(path));
|
||||
}
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
|
||||
uint32* count)
|
||||
{
|
||||
uint32 bcount = fReply->Stream().GetUInt();
|
||||
uint32* bitmap = new(std::nothrow) uint32[bcount];
|
||||
if (bitmap == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint32 attr_count = 0;
|
||||
for (uint32 i = 0; i < bcount; i++) {
|
||||
bitmap[i] = str.GetUInt();
|
||||
attr_count += CountBits(bitmap[i]);
|
||||
}
|
||||
|
||||
if (attr_count == 0) {
|
||||
*attrs = NULL;
|
||||
*count = 0;
|
||||
return B_OK;
|
||||
} else if (attr_count > FATTR4_MAXIMUM_ATTR_ID)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
uint32 size;
|
||||
const void* ptr = str.GetOpaque(&size);
|
||||
XDR::ReadStream stream(const_cast<void*>(ptr), size);
|
||||
|
||||
AttrValue* values = new(std::nothrow) AttrValue[attr_count];
|
||||
if (values == NULL) {
|
||||
delete[] bitmap;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
uint32 current = 0;
|
||||
|
||||
if (sIsAttrSet(FATTR4_SUPPORTED_ATTRS, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_SUPPORTED_ATTRS;
|
||||
uint32 count = stream.GetInt();
|
||||
uint32 i;
|
||||
// two uint32 are enough for NFS4, not for NFS4.1
|
||||
for (i = 0; i < min_c(count, 2); i++)
|
||||
((uint32*)&values[current].fData.fValue64)[i] = stream.GetUInt();
|
||||
for (; i < count; i++)
|
||||
stream.GetUInt();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_TYPE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_TYPE;
|
||||
values[current].fData.fValue32 = stream.GetInt();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_FH_EXPIRE_TYPE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_FH_EXPIRE_TYPE;
|
||||
values[current].fData.fValue32 = stream.GetUInt();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_CHANGE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_CHANGE;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_SIZE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_SIZE;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_FSID, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_FSID;
|
||||
values[current].fFreePointer = true;
|
||||
|
||||
FileSystemId fsid;
|
||||
fsid.fMajor = stream.GetUHyper();
|
||||
fsid.fMinor = stream.GetUHyper();
|
||||
|
||||
values[current].fData.fPointer = malloc(sizeof(fsid));
|
||||
memcpy(values[current].fData.fPointer, &fsid, sizeof(fsid));
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_LEASE_TIME, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_LEASE_TIME;
|
||||
values[current].fData.fValue32 = stream.GetUInt();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_FILEID, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_FILEID;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_FILES_FREE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_FILES_FREE;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_FILES_TOTAL, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_FILES_TOTAL;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_FS_LOCATIONS, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_FS_LOCATIONS;
|
||||
|
||||
FSLocations* locs = new FSLocations;
|
||||
locs->fRootPath = sFlattenPathname(stream);
|
||||
locs->fCount = stream.GetUInt();
|
||||
locs->fLocations = new FSLocation[locs->fCount];
|
||||
for (uint32 i = 0; i < locs->fCount; i++) {
|
||||
locs->fLocations[i].fRootPath = sFlattenPathname(stream);
|
||||
locs->fLocations[i].fCount = stream.GetUInt();
|
||||
locs->fLocations[i].fLocations
|
||||
= new const char*[locs->fLocations[i].fCount];
|
||||
for (uint32 j = 0; j < locs->fLocations[i].fCount; j++)
|
||||
locs->fLocations[i].fLocations[j] = stream.GetString();
|
||||
}
|
||||
values[current].fData.fLocations = locs;
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_MAXREAD, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_MAXREAD;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_MAXWRITE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_MAXWRITE;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_MODE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_MODE;
|
||||
values[current].fData.fValue32 = stream.GetUInt();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_NUMLINKS, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_NUMLINKS;
|
||||
values[current].fData.fValue32 = stream.GetUInt();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_OWNER, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_OWNER;
|
||||
values[current].fFreePointer = true;
|
||||
values[current].fData.fPointer = stream.GetString();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_OWNER_GROUP, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_OWNER_GROUP;
|
||||
values[current].fFreePointer = true;
|
||||
values[current].fData.fPointer = stream.GetString();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_SPACE_FREE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_SPACE_FREE;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_SPACE_TOTAL, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_SPACE_TOTAL;
|
||||
values[current].fData.fValue64 = stream.GetUHyper();
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_TIME_ACCESS, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_TIME_ACCESS;
|
||||
values[current].fFreePointer = true;
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(stream.GetHyper());
|
||||
ts.tv_nsec = static_cast<long>(stream.GetUInt());
|
||||
|
||||
values[current].fData.fPointer = malloc(sizeof(ts));
|
||||
memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_TIME_CREATE, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_TIME_CREATE;
|
||||
values[current].fFreePointer = true;
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(stream.GetHyper());
|
||||
ts.tv_nsec = static_cast<long>(stream.GetUInt());
|
||||
|
||||
values[current].fData.fPointer = malloc(sizeof(ts));
|
||||
memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_TIME_METADATA, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_TIME_METADATA;
|
||||
values[current].fFreePointer = true;
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(stream.GetHyper());
|
||||
ts.tv_nsec = static_cast<long>(stream.GetUInt());
|
||||
|
||||
values[current].fData.fPointer = malloc(sizeof(ts));
|
||||
memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
|
||||
current++;
|
||||
}
|
||||
|
||||
if (sIsAttrSet(FATTR4_TIME_MODIFY, bitmap, bcount)) {
|
||||
values[current].fAttribute = FATTR4_TIME_MODIFY;
|
||||
values[current].fFreePointer = true;
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(stream.GetHyper());
|
||||
ts.tv_nsec = static_cast<long>(stream.GetUInt());
|
||||
|
||||
values[current].fData.fPointer = malloc(sizeof(ts));
|
||||
memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
|
||||
current++;
|
||||
}
|
||||
|
||||
delete[] bitmap;
|
||||
|
||||
*count = attr_count;
|
||||
*attrs = values;
|
||||
return str.IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::_OperationError(Opcode op)
|
||||
{
|
||||
if (fDecodeError)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (fReply == NULL)
|
||||
return B_NOT_INITIALIZED;
|
||||
|
||||
if (fReply->Error() != B_OK || fReply->Stream().IsEOF()) {
|
||||
fDecodeError = true;
|
||||
return fReply->Error();
|
||||
}
|
||||
|
||||
if (fReply->Stream().GetInt() != op) {
|
||||
fDecodeError = true;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t result = _NFS4ErrorToHaiku(fReply->Stream().GetUInt());
|
||||
if (result != B_OK)
|
||||
fDecodeError = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x)
|
||||
{
|
||||
switch (x) {
|
||||
case NFS4_OK: return B_OK;
|
||||
case NFS4ERR_PERM: return B_PERMISSION_DENIED;
|
||||
case NFS4ERR_NOENT: return B_ENTRY_NOT_FOUND;
|
||||
case NFS4ERR_IO: return B_IO_ERROR;
|
||||
case NFS4ERR_NXIO: return B_DEVICE_NOT_FOUND;
|
||||
case NFS4ERR_ACCESS: return B_NOT_ALLOWED;
|
||||
case NFS4ERR_EXIST: return B_FILE_EXISTS;
|
||||
case NFS4ERR_XDEV: return B_CROSS_DEVICE_LINK;
|
||||
case NFS4ERR_NOTDIR: return B_NOT_A_DIRECTORY;
|
||||
case NFS4ERR_ISDIR: return B_IS_A_DIRECTORY;
|
||||
case NFS4ERR_INVAL: return B_BAD_VALUE;
|
||||
case NFS4ERR_FBIG: return B_FILE_TOO_LARGE;
|
||||
case NFS4ERR_NOTSUPP: return B_UNSUPPORTED;
|
||||
case NFS4ERR_ROFS: return B_READ_ONLY_DEVICE;
|
||||
case NFS4ERR_NAMETOOLONG: return B_NAME_TOO_LONG;
|
||||
case NFS4ERR_NOTEMPTY: return B_DIRECTORY_NOT_EMPTY;
|
||||
// ...
|
||||
case NFS4ERR_DELAY:
|
||||
case NFS4ERR_DENIED:
|
||||
case NFS4ERR_LOCKED:
|
||||
case NFS4ERR_GRACE:
|
||||
return B_WOULD_BLOCK;
|
||||
|
||||
case NFS4ERR_STALE:
|
||||
case NFS4ERR_FHEXPIRED:
|
||||
return B_FILE_NOT_FOUND;
|
||||
// ...
|
||||
default: return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef REPLYINTERPRETER_H
|
||||
#define REPLYINTERPRETER_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "FileInfo.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "RPCReply.h"
|
||||
|
||||
|
||||
struct FSLocation {
|
||||
const char* fRootPath;
|
||||
const char** fLocations;
|
||||
uint32 fCount;
|
||||
|
||||
~FSLocation();
|
||||
};
|
||||
|
||||
struct FSLocations {
|
||||
const char* fRootPath;
|
||||
FSLocation* fLocations;
|
||||
uint32 fCount;
|
||||
|
||||
~FSLocations();
|
||||
};
|
||||
|
||||
struct AttrValue {
|
||||
AttrValue();
|
||||
~AttrValue();
|
||||
|
||||
uint8 fAttribute;
|
||||
bool fFreePointer;
|
||||
union {
|
||||
uint32 fValue32;
|
||||
uint64 fValue64;
|
||||
void* fPointer;
|
||||
FSLocations* fLocations;
|
||||
} fData;
|
||||
};
|
||||
|
||||
struct DirEntry {
|
||||
const char* fName;
|
||||
AttrValue* fAttrs;
|
||||
uint32 fAttrCount;
|
||||
|
||||
DirEntry();
|
||||
~DirEntry();
|
||||
};
|
||||
|
||||
class LockInfo;
|
||||
|
||||
class ReplyInterpreter {
|
||||
public:
|
||||
ReplyInterpreter(RPC::Reply* reply = NULL);
|
||||
~ReplyInterpreter();
|
||||
|
||||
inline status_t SetTo(RPC::Reply* reply);
|
||||
inline void Reset();
|
||||
|
||||
inline uint32 NFS4Error();
|
||||
|
||||
status_t Access(uint32* supported, uint32* allowed);
|
||||
status_t Close();
|
||||
status_t Commit();
|
||||
status_t Create(uint64* before, uint64* after, bool& atomic);
|
||||
inline status_t DelegReturn();
|
||||
status_t GetAttr(AttrValue** attrs, uint32* count);
|
||||
status_t GetFH(FileHandle* fh);
|
||||
status_t Link(uint64* before, uint64* after, bool& atomic);
|
||||
status_t Lock(LockInfo* linfo);
|
||||
status_t LockT(uint64* pos, uint64* len, LockType* type);
|
||||
status_t LockU(LockInfo* linfo);
|
||||
inline status_t LookUp();
|
||||
inline status_t LookUpUp();
|
||||
inline status_t Nverify();
|
||||
status_t Open(uint32* id, uint32* seq, bool* confirm,
|
||||
OpenDelegationData* delegData,
|
||||
ChangeInfo* changeInfo = NULL);
|
||||
inline status_t OpenAttrDir();
|
||||
status_t OpenConfirm(uint32* stateSeq);
|
||||
inline status_t PutFH();
|
||||
inline status_t PutRootFH();
|
||||
status_t Read(void* buffer, uint32* size, bool* eof);
|
||||
status_t ReadDir(uint64* cookie, uint64* cookieVerf,
|
||||
DirEntry** dirents, uint32* count, bool* eof);
|
||||
status_t ReadLink(void* buffer, uint32* size, uint32 maxSize);
|
||||
status_t Remove(uint64* before, uint64* after, bool& atomic);
|
||||
status_t Rename(uint64* fromBefore, uint64* fromAfter,
|
||||
bool& fromAtomic, uint64* toBefore, uint64* toAfter,
|
||||
bool& toAtomic);
|
||||
inline status_t Renew();
|
||||
inline status_t SaveFH();
|
||||
status_t SetAttr();
|
||||
status_t SetClientID(uint64* clientid, uint64* verifier);
|
||||
inline status_t SetClientIDConfirm();
|
||||
inline status_t Verify();
|
||||
status_t Write(uint32* size);
|
||||
inline status_t ReleaseLockOwner();
|
||||
|
||||
private:
|
||||
void _ParseHeader();
|
||||
|
||||
status_t _DecodeAttrs(XDR::ReadStream& stream, AttrValue** attrs,
|
||||
uint32* count);
|
||||
status_t _OperationError(Opcode op);
|
||||
|
||||
static status_t _NFS4ErrorToHaiku(uint32 x);
|
||||
|
||||
uint32 fNFS4Error;
|
||||
bool fDecodeError;
|
||||
RPC::Reply* fReply;
|
||||
};
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::SetTo(RPC::Reply* _reply)
|
||||
{
|
||||
if (fReply != NULL)
|
||||
return B_DONT_DO_THAT;
|
||||
|
||||
fDecodeError = false;
|
||||
fReply = _reply;
|
||||
|
||||
if (fReply != NULL)
|
||||
_ParseHeader();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
ReplyInterpreter::Reset()
|
||||
{
|
||||
delete fReply;
|
||||
fReply = NULL;
|
||||
fDecodeError = false;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
ReplyInterpreter::NFS4Error()
|
||||
{
|
||||
return fNFS4Error;
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::DelegReturn()
|
||||
{
|
||||
return _OperationError(OpDelegReturn);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::LookUp()
|
||||
{
|
||||
return _OperationError(OpLookUp);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::LookUpUp()
|
||||
{
|
||||
return _OperationError(OpLookUpUp);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::OpenAttrDir()
|
||||
{
|
||||
return _OperationError(OpOpenAttrDir);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::Nverify()
|
||||
{
|
||||
return _OperationError(OpNverify);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::PutFH()
|
||||
{
|
||||
return _OperationError(OpPutFH);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::PutRootFH()
|
||||
{
|
||||
return _OperationError(OpPutRootFH);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::Renew()
|
||||
{
|
||||
return _OperationError(OpRenew);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::SaveFH()
|
||||
{
|
||||
return _OperationError(OpSaveFH);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::SetClientIDConfirm()
|
||||
{
|
||||
return _OperationError(OpSetClientIDConfirm);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::Verify()
|
||||
{
|
||||
return _OperationError(OpVerify);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
ReplyInterpreter::ReleaseLockOwner()
|
||||
{
|
||||
return _OperationError(OpReleaseLockOwner);
|
||||
}
|
||||
|
||||
|
||||
#endif // REPLYINTERPRETER_H
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "Request.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
status_t
|
||||
Request::Send(Cookie* cookie)
|
||||
{
|
||||
switch (fServer->ID().fProtocol) {
|
||||
case IPPROTO_UDP: return _SendUDP(cookie);
|
||||
case IPPROTO_TCP: return _SendTCP(cookie);
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Request::_SendUDP(Cookie* cookie)
|
||||
{
|
||||
RPC::Reply* rpl = NULL;
|
||||
RPC::Request* rpc;
|
||||
|
||||
status_t result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if (cookie != NULL)
|
||||
cookie->RegisterRequest(rpc);
|
||||
|
||||
int requestTimeout = sSecToBigTime(60);
|
||||
int retryLimit = 0;
|
||||
bool hard = true;
|
||||
|
||||
if (fFileSystem != NULL) {
|
||||
requestTimeout = fFileSystem->GetConfiguration().fRequestTimeout;
|
||||
retryLimit = fFileSystem->GetConfiguration().fRetryLimit;
|
||||
hard = fFileSystem->GetConfiguration().fHard;
|
||||
}
|
||||
|
||||
result = fServer->WaitCall(rpc, requestTimeout);
|
||||
if (result != B_OK) {
|
||||
int attempts = 1;
|
||||
while (result != B_OK && (hard || attempts++ < retryLimit)) {
|
||||
result = fServer->ResendCallAsync(fBuilder.Request(), rpc);
|
||||
if (result != B_OK) {
|
||||
if (cookie != NULL)
|
||||
cookie->UnregisterRequest(rpc);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = fServer->WaitCall(rpc, requestTimeout);
|
||||
}
|
||||
|
||||
if (result != B_OK) {
|
||||
if (cookie != NULL)
|
||||
cookie->UnregisterRequest(rpc);
|
||||
fServer->CancelCall(rpc);
|
||||
delete rpc;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (cookie != NULL)
|
||||
cookie->UnregisterRequest(rpc);
|
||||
|
||||
if (rpc->fError != B_OK) {
|
||||
delete rpl;
|
||||
result = rpc->fError;
|
||||
delete rpc;
|
||||
return result;
|
||||
} else {
|
||||
fReply.SetTo(rpl);
|
||||
delete rpc;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Request::_SendTCP(Cookie* cookie)
|
||||
{
|
||||
RPC::Reply* rpl = NULL;
|
||||
RPC::Request* rpc;
|
||||
|
||||
status_t result;
|
||||
int attempts = 0;
|
||||
|
||||
int requestTimeout = sSecToBigTime(60);
|
||||
int retryLimit = 0;
|
||||
bool hard = true;
|
||||
|
||||
if (fFileSystem != NULL) {
|
||||
requestTimeout = fFileSystem->GetConfiguration().fRequestTimeout;
|
||||
retryLimit = fFileSystem->GetConfiguration().fRetryLimit;
|
||||
hard = fFileSystem->GetConfiguration().fHard;
|
||||
}
|
||||
|
||||
do {
|
||||
result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
|
||||
if (result == B_NO_MEMORY)
|
||||
return result;
|
||||
else if (result != B_OK) {
|
||||
fServer->Repair();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cookie != NULL)
|
||||
cookie->RegisterRequest(rpc);
|
||||
|
||||
result = fServer->WaitCall(rpc, requestTimeout);
|
||||
if (result != B_OK) {
|
||||
if (cookie != NULL)
|
||||
cookie->UnregisterRequest(rpc);
|
||||
|
||||
fServer->CancelCall(rpc);
|
||||
delete rpc;
|
||||
|
||||
fServer->Repair();
|
||||
}
|
||||
} while (result != B_OK && (hard || attempts++ < retryLimit));
|
||||
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if (cookie != NULL)
|
||||
cookie->UnregisterRequest(rpc);
|
||||
|
||||
if (rpc->fError != B_OK) {
|
||||
delete rpl;
|
||||
result = rpc->fError;
|
||||
delete rpc;
|
||||
return result;
|
||||
}
|
||||
|
||||
fReply.SetTo(rpl);
|
||||
delete rpc;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Request::Reset()
|
||||
{
|
||||
fBuilder.Reset();
|
||||
fReply.Reset();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef REQUEST_H
|
||||
#define REQUEST_H
|
||||
|
||||
|
||||
#include "ReplyInterpreter.h"
|
||||
#include "RequestBuilder.h"
|
||||
#include "RPCServer.h"
|
||||
|
||||
|
||||
class Cookie;
|
||||
class FileSystem;
|
||||
|
||||
class Request {
|
||||
public:
|
||||
inline Request(RPC::Server* server,
|
||||
FileSystem* fileSystem);
|
||||
|
||||
inline RequestBuilder& Builder();
|
||||
inline ReplyInterpreter& Reply();
|
||||
|
||||
status_t Send(Cookie* cookie = NULL);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
status_t _SendUDP(Cookie* cookie);
|
||||
status_t _SendTCP(Cookie* cookie);
|
||||
|
||||
RPC::Server* fServer;
|
||||
FileSystem* fFileSystem;
|
||||
|
||||
RequestBuilder fBuilder;
|
||||
ReplyInterpreter fReply;
|
||||
};
|
||||
|
||||
|
||||
inline
|
||||
Request::Request(RPC::Server* server, FileSystem* fileSystem)
|
||||
:
|
||||
fServer(server),
|
||||
fFileSystem(fileSystem)
|
||||
{
|
||||
ASSERT(server != NULL);
|
||||
}
|
||||
|
||||
|
||||
inline RequestBuilder&
|
||||
Request::Builder()
|
||||
{
|
||||
return fBuilder;
|
||||
}
|
||||
|
||||
|
||||
inline ReplyInterpreter&
|
||||
Request::Reply()
|
||||
{
|
||||
return fReply;
|
||||
}
|
||||
|
||||
|
||||
#endif // REQUEST_H
|
||||
|
||||
@@ -0,0 +1,917 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RequestBuilder.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Cookie.h"
|
||||
#include "OpenState.h"
|
||||
#include "RPCCallback.h"
|
||||
#include "RPCCallbackServer.h"
|
||||
|
||||
|
||||
RequestBuilder::RequestBuilder(Procedure proc)
|
||||
:
|
||||
fOpCount(0),
|
||||
fProcedure(proc),
|
||||
fRequest(NULL)
|
||||
{
|
||||
_InitHeader();
|
||||
}
|
||||
|
||||
|
||||
RequestBuilder::~RequestBuilder()
|
||||
{
|
||||
delete fRequest;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RequestBuilder::_InitHeader()
|
||||
{
|
||||
fRequest = RPC::Call::Create(fProcedure, RPC::Auth::CreateSys(),
|
||||
RPC::Auth::CreateNone());
|
||||
|
||||
if (fRequest == NULL)
|
||||
return;
|
||||
|
||||
if (fProcedure == ProcCompound) {
|
||||
fRequest->Stream().AddOpaque(NULL, 0);
|
||||
fRequest->Stream().AddUInt(0);
|
||||
|
||||
fOpCountPosition = fRequest->Stream().Current();
|
||||
fRequest->Stream().AddUInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Access()
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpAccess);
|
||||
fRequest->Stream().AddUInt(ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY
|
||||
| ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE);
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Close(uint32 seq, const uint32* id, uint32 stateSeq)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpClose);
|
||||
fRequest->Stream().AddUInt(seq);
|
||||
fRequest->Stream().AddUInt(stateSeq);
|
||||
fRequest->Stream().AddUInt(id[0]);
|
||||
fRequest->Stream().AddUInt(id[1]);
|
||||
fRequest->Stream().AddUInt(id[2]);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Commit(uint64 offset, uint32 count)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpCommit);
|
||||
fRequest->Stream().AddUHyper(offset);
|
||||
fRequest->Stream().AddUInt(count);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Create(FileType type, const char* name, AttrValue* attr,
|
||||
uint32 count, const char* path)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
if (type == NF4LNK && path == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (name == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (type == NF4BLK || type == NF4CHR)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
fRequest->Stream().AddUInt(OpCreate);
|
||||
fRequest->Stream().AddUInt(type);
|
||||
if (type == NF4LNK)
|
||||
fRequest->Stream().AddString(path);
|
||||
fRequest->Stream().AddString(name);
|
||||
_EncodeAttrs(fRequest->Stream(), attr, count);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::DelegReturn(const uint32* id, uint32 seq)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpDelegReturn);
|
||||
|
||||
fRequest->Stream().AddUInt(seq);
|
||||
fRequest->Stream().AddUInt(id[0]);
|
||||
fRequest->Stream().AddUInt(id[1]);
|
||||
fRequest->Stream().AddUInt(id[2]);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::GetAttr(Attribute* attrs, uint32 count)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpGetAttr);
|
||||
_AttrBitmap(fRequest->Stream(), attrs, count);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::GetFH()
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpGetFH);
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RequestBuilder::_GenerateLockOwner(XDR::WriteStream& stream,
|
||||
OpenState* state, LockOwner* owner)
|
||||
{
|
||||
stream.AddUHyper(state->fClientID);
|
||||
|
||||
uint64 lockOwner[2];
|
||||
lockOwner[0] = owner->fOwner;
|
||||
lockOwner[1] = state->fInfo.fFileId;
|
||||
stream.AddOpaque(lockOwner, sizeof(lockOwner));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Lock(OpenState* state, LockInfo* lock, uint32* sequence,
|
||||
bool reclaim)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpLock);
|
||||
|
||||
fRequest->Stream().AddInt(lock->fType);
|
||||
fRequest->Stream().AddBoolean(reclaim);
|
||||
|
||||
fRequest->Stream().AddUHyper(lock->fStart);
|
||||
fRequest->Stream().AddUHyper(lock->fLength);
|
||||
|
||||
if (lock->fOwner->fStateId[0] == 0 && lock->fOwner->fStateId[1] == 0
|
||||
&& lock->fOwner->fStateId[2] == 0) {
|
||||
|
||||
fRequest->Stream().AddBoolean(true); // new lock owner
|
||||
|
||||
// open seq stateid
|
||||
fRequest->Stream().AddUInt(*sequence);
|
||||
fRequest->Stream().AddUInt(state->fStateSeq);
|
||||
fRequest->Stream().AddUInt(state->fStateID[0]);
|
||||
fRequest->Stream().AddUInt(state->fStateID[1]);
|
||||
fRequest->Stream().AddUInt(state->fStateID[2]);
|
||||
|
||||
// lock seq owner
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
|
||||
_GenerateLockOwner(fRequest->Stream(), state, lock->fOwner);
|
||||
|
||||
} else {
|
||||
fRequest->Stream().AddBoolean(false); // old lock owner
|
||||
(*sequence)--;
|
||||
|
||||
// lock stateid seq
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
|
||||
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
|
||||
}
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::LockT(LockType type, uint64 pos, uint64 len,
|
||||
OpenState* state)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpLockT);
|
||||
|
||||
fRequest->Stream().AddInt(type);
|
||||
|
||||
fRequest->Stream().AddUHyper(pos);
|
||||
fRequest->Stream().AddUHyper(len);
|
||||
|
||||
fRequest->Stream().AddUHyper(state->fClientID);
|
||||
|
||||
uint32 owner = find_thread(NULL);
|
||||
fRequest->Stream().AddOpaque(&owner, sizeof(owner));
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::LockU(LockInfo* lock)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpLockU);
|
||||
|
||||
fRequest->Stream().AddInt(lock->fType);
|
||||
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
|
||||
fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
|
||||
|
||||
fRequest->Stream().AddUHyper(lock->fStart);
|
||||
fRequest->Stream().AddUHyper(lock->fLength);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Link(const char* name)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
if (name == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
fRequest->Stream().AddUInt(OpLink);
|
||||
fRequest->Stream().AddString(name);
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::LookUp(const char* name)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
if (name == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
fRequest->Stream().AddUInt(OpLookUp);
|
||||
fRequest->Stream().AddString(name, strlen(name));
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::LookUpUp()
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpLookUpUp);
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Nverify(AttrValue* attr, uint32 count)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpNverify);
|
||||
_EncodeAttrs(fRequest->Stream(), attr, count);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Open(OpenClaim claim, uint32 seq, uint32 access, uint64 id,
|
||||
OpenCreate oc, uint64 ownerId, const char* name, AttrValue* attr,
|
||||
uint32 count, bool excl, OpenDelegation delegationType)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpOpen);
|
||||
fRequest->Stream().AddUInt(seq);
|
||||
fRequest->Stream().AddUInt(access);
|
||||
fRequest->Stream().AddUInt(0); // deny none
|
||||
fRequest->Stream().AddUHyper(id);
|
||||
|
||||
char owner[128];
|
||||
int pos = 0;
|
||||
*(uint64*)(owner + pos) = ownerId;
|
||||
pos += sizeof(uint64);
|
||||
|
||||
fRequest->Stream().AddOpaque(owner, pos);
|
||||
|
||||
fRequest->Stream().AddUInt(oc);
|
||||
if (oc == OPEN4_CREATE) {
|
||||
fRequest->Stream().AddInt(excl ? GUARDED4 : UNCHECKED4);
|
||||
_EncodeAttrs(fRequest->Stream(), attr, count);
|
||||
}
|
||||
|
||||
fRequest->Stream().AddUInt(claim);
|
||||
switch (claim) {
|
||||
case CLAIM_NULL:
|
||||
fRequest->Stream().AddString(name, strlen(name));
|
||||
break;
|
||||
case CLAIM_PREVIOUS:
|
||||
fRequest->Stream().AddUInt(delegationType);
|
||||
break;
|
||||
default:
|
||||
return B_UNSUPPORTED;
|
||||
}
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::OpenConfirm(uint32 seq, const uint32* id, uint32 stateSeq)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpOpenConfirm);
|
||||
fRequest->Stream().AddUInt(stateSeq);
|
||||
fRequest->Stream().AddUInt(id[0]);
|
||||
fRequest->Stream().AddUInt(id[1]);
|
||||
fRequest->Stream().AddUInt(id[2]);
|
||||
fRequest->Stream().AddUInt(seq);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::OpenAttrDir(bool create)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpOpenAttrDir);
|
||||
fRequest->Stream().AddBoolean(create);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::PutFH(const FileHandle& fh)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpPutFH);
|
||||
fRequest->Stream().AddOpaque(fh.fData, fh.fSize);
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::PutRootFH()
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpPutRootFH);
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Read(const uint32* id, uint32 stateSeq, uint64 pos, uint32 len)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpRead);
|
||||
fRequest->Stream().AddUInt(stateSeq);
|
||||
fRequest->Stream().AddUInt(id[0]);
|
||||
fRequest->Stream().AddUInt(id[1]);
|
||||
fRequest->Stream().AddUInt(id[2]);
|
||||
fRequest->Stream().AddUHyper(pos);
|
||||
fRequest->Stream().AddUInt(len);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::ReadDir(uint32 count, uint64 cookie, uint64 cookieVerf,
|
||||
Attribute* attrs, uint32 attrCount)
|
||||
{
|
||||
(void)count;
|
||||
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpReadDir);
|
||||
fRequest->Stream().AddUHyper(cookie);
|
||||
fRequest->Stream().AddUHyper(cookieVerf);
|
||||
|
||||
// consider predicting this values basing on count or buffer size
|
||||
fRequest->Stream().AddUInt(0x2000);
|
||||
fRequest->Stream().AddUInt(0x8000);
|
||||
_AttrBitmap(fRequest->Stream(), attrs, attrCount);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::ReadLink()
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpReadLink);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Remove(const char* file)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpRemove);
|
||||
fRequest->Stream().AddString(file);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Rename(const char* from, const char* to)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpRename);
|
||||
fRequest->Stream().AddString(from);
|
||||
fRequest->Stream().AddString(to);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Renew(uint64 clientId)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpRenew);
|
||||
fRequest->Stream().AddUHyper(clientId);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::SaveFH()
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpSaveFH);
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::SetAttr(const uint32* id, uint32 stateSeq, AttrValue* attr,
|
||||
uint32 count)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpSetAttr);
|
||||
fRequest->Stream().AddUInt(stateSeq);
|
||||
if (id != NULL) {
|
||||
fRequest->Stream().AddUInt(id[0]);
|
||||
fRequest->Stream().AddUInt(id[1]);
|
||||
fRequest->Stream().AddUInt(id[2]);
|
||||
} else {
|
||||
fRequest->Stream().AddUInt(0);
|
||||
fRequest->Stream().AddUInt(0);
|
||||
fRequest->Stream().AddUInt(0);
|
||||
}
|
||||
_EncodeAttrs(fRequest->Stream(), attr, count);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::SetClientID(RPC::Server* server)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpSetClientID);
|
||||
uint64 verifier = rand();
|
||||
verifier = verifier << 32 | rand();
|
||||
fRequest->Stream().AddUHyper(verifier);
|
||||
|
||||
status_t result = _GenerateClientId(fRequest->Stream(), server);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fRequest->Stream().AddUInt(0x40000000);
|
||||
|
||||
if (server->GetCallback() != NULL) {
|
||||
ASSERT(server->GetCallback()->CBServer() != NULL);
|
||||
|
||||
uint32 id = server->GetCallback()->ID();
|
||||
|
||||
PeerAddress local = server->GetCallback()->CBServer()->LocalID();
|
||||
PeerAddress servAddr = server->LocalID();
|
||||
servAddr.SetPort(local.Port());
|
||||
|
||||
fRequest->Stream().AddString(local.ProtocolString());
|
||||
|
||||
char* uAddr = servAddr.UniversalAddress();
|
||||
if (uAddr == NULL)
|
||||
return B_NO_MEMORY;
|
||||
fRequest->Stream().AddString(uAddr);
|
||||
free(uAddr);
|
||||
|
||||
fRequest->Stream().AddUInt(id);
|
||||
} else {
|
||||
fRequest->Stream().AddString("");
|
||||
fRequest->Stream().AddString("");
|
||||
fRequest->Stream().AddUInt(0);
|
||||
}
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::_GenerateClientId(XDR::WriteStream& stream,
|
||||
const RPC::Server* server)
|
||||
{
|
||||
char id[512] = "HAIKU:kernel:";
|
||||
int pos = strlen(id);
|
||||
|
||||
PeerAddress local = server->LocalID();
|
||||
|
||||
memcpy(id + pos, server->ID().InAddr(), server->ID().InAddrSize());
|
||||
pos += sizeof(server->ID().InAddrSize());
|
||||
|
||||
memcpy(id + pos, local.InAddr(), local.InAddrSize());
|
||||
pos += sizeof(local.InAddrSize());
|
||||
|
||||
*(uint16*)(id + pos) = server->ID().Port();
|
||||
pos += sizeof(uint16);
|
||||
|
||||
*(uint16*)(id + pos) = server->ID().fProtocol;
|
||||
pos += sizeof(uint16);
|
||||
|
||||
stream.AddOpaque(id, pos);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::SetClientIDConfirm(uint64 id, uint64 ver)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpSetClientIDConfirm);
|
||||
fRequest->Stream().AddUHyper(id);
|
||||
fRequest->Stream().AddUHyper(ver);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Verify(AttrValue* attr, uint32 count)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpVerify);
|
||||
_EncodeAttrs(fRequest->Stream(), attr, count);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::Write(const uint32* id, uint32 stateSeq, const void* buffer,
|
||||
uint64 pos, uint32 len, bool stable)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpWrite);
|
||||
fRequest->Stream().AddUInt(stateSeq);
|
||||
fRequest->Stream().AddUInt(id[0]);
|
||||
fRequest->Stream().AddUInt(id[1]);
|
||||
fRequest->Stream().AddUInt(id[2]);
|
||||
fRequest->Stream().AddUHyper(pos);
|
||||
fRequest->Stream().AddInt(stable ? FILE_SYNC4 : UNSTABLE4);
|
||||
fRequest->Stream().AddOpaque(buffer, len);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestBuilder::ReleaseLockOwner(OpenState* state, LockOwner* owner)
|
||||
{
|
||||
if (fProcedure != ProcCompound)
|
||||
return B_BAD_VALUE;
|
||||
if (fRequest == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fRequest->Stream().AddUInt(OpReleaseLockOwner);
|
||||
_GenerateLockOwner(fRequest->Stream(), state, owner);
|
||||
|
||||
fOpCount++;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
RPC::Call*
|
||||
RequestBuilder::Request()
|
||||
{
|
||||
if (fProcedure == ProcCompound)
|
||||
fRequest->Stream().InsertUInt(fOpCountPosition, fOpCount);
|
||||
|
||||
if (fRequest == NULL || fRequest->Stream().Error() == B_OK)
|
||||
return fRequest;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RequestBuilder::_AttrBitmap(XDR::WriteStream& stream, Attribute* attrs,
|
||||
uint32 count)
|
||||
{
|
||||
// 2 is safe in NFS4, not in NFS4.1 though
|
||||
uint32 bitmap[2];
|
||||
memset(bitmap, 0, sizeof(bitmap));
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
bitmap[attrs[i] / 32] |= 1 << attrs[i] % 32;
|
||||
}
|
||||
|
||||
uint32 bcount = bitmap[1] != 0 ? 2 : 1;
|
||||
stream.AddUInt(bcount);
|
||||
for (uint32 i = 0; i < bcount; i++)
|
||||
stream.AddUInt(bitmap[i]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RequestBuilder::_EncodeAttrs(XDR::WriteStream& stream, AttrValue* attr,
|
||||
uint32 count)
|
||||
{
|
||||
if (count == 0) {
|
||||
stream.AddUInt(0);
|
||||
stream.AddOpaque(NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Attribute* attrs
|
||||
= reinterpret_cast<Attribute*>(malloc(sizeof(Attribute) * count));
|
||||
for (uint32 i = 0; i < count; i++)
|
||||
attrs[i] = static_cast<Attribute>(attr[i].fAttribute);
|
||||
_AttrBitmap(stream, attrs, count);
|
||||
free(attrs);
|
||||
|
||||
uint32 i = 0;
|
||||
XDR::WriteStream str;
|
||||
if (i < count && attr[i].fAttribute == FATTR4_TYPE) {
|
||||
str.AddUInt(attr[i].fData.fValue32);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_SIZE) {
|
||||
str.AddUHyper(attr[i].fData.fValue64);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_FILEHANDLE) {
|
||||
FileHandle* fh = reinterpret_cast<FileHandle*>(attr[i].fData.fPointer);
|
||||
str.AddOpaque(fh->fData, fh->fSize);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_FILEID) {
|
||||
str.AddUHyper(attr[i].fData.fValue64);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_MODE) {
|
||||
str.AddUInt(attr[i].fData.fValue32);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_OWNER) {
|
||||
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_OWNER_GROUP) {
|
||||
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_TIME_ACCESS_SET) {
|
||||
str.AddInt(1); // SET_TO_CLIENT_TIME4
|
||||
|
||||
struct timespec* ts
|
||||
= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
|
||||
str.AddHyper(ts->tv_sec);
|
||||
str.AddUInt(ts->tv_nsec);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < count && attr[i].fAttribute == FATTR4_TIME_MODIFY_SET) {
|
||||
str.AddInt(1); // SET_TO_CLIENT_TIME4
|
||||
|
||||
struct timespec* ts
|
||||
= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
|
||||
str.AddHyper(ts->tv_sec);
|
||||
str.AddUInt(ts->tv_nsec);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
stream.AddOpaque(str);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef REQUESTBUILDER_H
|
||||
#define REQUESTBUILDER_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "FileInfo.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "ReplyInterpreter.h"
|
||||
#include "RPCCall.h"
|
||||
#include "RPCServer.h"
|
||||
#include "XDR.h"
|
||||
|
||||
|
||||
class OpenState;
|
||||
class LockInfo;
|
||||
class LockOwner;
|
||||
|
||||
class RequestBuilder {
|
||||
public:
|
||||
RequestBuilder(Procedure p = ProcCompound);
|
||||
~RequestBuilder();
|
||||
|
||||
inline void Reset(Procedure proc = ProcCompound);
|
||||
|
||||
status_t Access();
|
||||
status_t Close(uint32 seq, const uint32* id,
|
||||
uint32 stateSeq);
|
||||
status_t Commit(uint64 offset, uint32 count);
|
||||
status_t Create(FileType type, const char* name,
|
||||
AttrValue* attr, uint32 count,
|
||||
const char* path = NULL);
|
||||
status_t DelegReturn(const uint32* id, uint32 seq);
|
||||
status_t GetAttr(Attribute* attrs, uint32 count);
|
||||
status_t GetFH();
|
||||
status_t Link(const char* name);
|
||||
status_t Lock(OpenState* state, LockInfo* lock,
|
||||
uint32* sequence, bool reclaim = false);
|
||||
status_t LockT(LockType type, uint64 pos,
|
||||
uint64 len, OpenState* state);
|
||||
status_t LockU(LockInfo* lock);
|
||||
status_t LookUp(const char* name);
|
||||
status_t LookUpUp();
|
||||
status_t Nverify(AttrValue* attr, uint32 count);
|
||||
status_t Open(OpenClaim claim, uint32 seq,
|
||||
uint32 access, uint64 id, OpenCreate oc,
|
||||
uint64 ownerId, const char* name,
|
||||
AttrValue* attr = NULL,
|
||||
uint32 count = 0, bool excl = false,
|
||||
OpenDelegation delegType
|
||||
= OPEN_DELEGATE_NONE);
|
||||
status_t OpenAttrDir(bool create);
|
||||
status_t OpenConfirm(uint32 seq, const uint32* id,
|
||||
uint32 stateSeq);
|
||||
status_t PutFH(const FileHandle& fh);
|
||||
status_t PutRootFH();
|
||||
status_t Read(const uint32* id, uint32 stateSeq,
|
||||
uint64 pos, uint32 len);
|
||||
status_t ReadDir(uint32 count, uint64 cookie,
|
||||
uint64 cookieVerf, Attribute* attrs,
|
||||
uint32 attrCount);
|
||||
status_t ReadLink();
|
||||
status_t Remove(const char* file);
|
||||
status_t Rename(const char* from, const char* to);
|
||||
status_t Renew(uint64 clientId);
|
||||
status_t SaveFH();
|
||||
status_t SetAttr(const uint32* id, uint32 stateSeq,
|
||||
AttrValue* attr, uint32 count);
|
||||
status_t SetClientID(RPC::Server* server);
|
||||
status_t SetClientIDConfirm(uint64 id, uint64 ver);
|
||||
status_t Verify(AttrValue* attr, uint32 count);
|
||||
status_t Write(const uint32* id, uint32 stateSeq,
|
||||
const void* buffer, uint64 pos,
|
||||
uint32 len, bool stable = false);
|
||||
status_t ReleaseLockOwner(OpenState* state,
|
||||
LockOwner* owner);
|
||||
|
||||
RPC::Call* Request();
|
||||
|
||||
private:
|
||||
void _InitHeader();
|
||||
|
||||
void _GenerateLockOwner(XDR::WriteStream& stream,
|
||||
OpenState* state, LockOwner* owner);
|
||||
status_t _GenerateClientId(XDR::WriteStream& stream,
|
||||
const RPC::Server* server);
|
||||
|
||||
void _EncodeAttrs(XDR::WriteStream& stream,
|
||||
AttrValue* attr, uint32 count);
|
||||
void _AttrBitmap(XDR::WriteStream& stream,
|
||||
Attribute* attrs, uint32 count);
|
||||
|
||||
uint32 fOpCount;
|
||||
XDR::Stream::Position fOpCountPosition;
|
||||
|
||||
Procedure fProcedure;
|
||||
|
||||
RPC::Call* fRequest;
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
RequestBuilder::Reset(Procedure proc)
|
||||
{
|
||||
fRequest->Stream().Clear();
|
||||
fOpCount = 0;
|
||||
fProcedure = proc;
|
||||
delete fRequest;
|
||||
|
||||
_InitHeader();
|
||||
}
|
||||
|
||||
|
||||
#endif // REQUESTBUILDER_H
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RequestInterpreter.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <util/kernel_cpp.h>
|
||||
|
||||
|
||||
RequestInterpreter::RequestInterpreter(RPC::CallbackRequest* request)
|
||||
:
|
||||
fRequest(request)
|
||||
{
|
||||
fOperationCount = fRequest->Stream().GetUInt();
|
||||
}
|
||||
|
||||
|
||||
RequestInterpreter::~RequestInterpreter()
|
||||
{
|
||||
delete fRequest;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestInterpreter::GetAttr(FileHandle* handle, int* _mask)
|
||||
{
|
||||
if (fLastOperation != OpCallbackGetAttr)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
uint32 size;
|
||||
const void* ptr = fRequest->Stream().GetOpaque(&size);
|
||||
handle->fSize = size;
|
||||
memcpy(handle->fData, ptr, size);
|
||||
|
||||
uint32 count = fRequest->Stream().GetUInt();
|
||||
if (count < 1) {
|
||||
*_mask = 0;
|
||||
return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
uint32 bitmap = fRequest->Stream().GetUInt();
|
||||
uint32 mask = 0;
|
||||
|
||||
if ((bitmap & (1 << FATTR4_CHANGE)) != 0)
|
||||
mask |= CallbackAttrChange;
|
||||
if ((bitmap & (1 << FATTR4_SIZE)) != 0)
|
||||
mask |= CallbackAttrSize;
|
||||
|
||||
*_mask = mask;
|
||||
|
||||
for (uint32 i = 1; i < count; i++)
|
||||
fRequest->Stream().GetUInt();
|
||||
|
||||
return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RequestInterpreter::Recall(FileHandle* handle, bool& truncate, uint32* stateSeq,
|
||||
uint32* stateID)
|
||||
{
|
||||
if (fLastOperation != OpCallbackRecall)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*stateSeq = fRequest->Stream().GetUInt();
|
||||
stateID[0] = fRequest->Stream().GetUInt();
|
||||
stateID[1] = fRequest->Stream().GetUInt();
|
||||
stateID[2] = fRequest->Stream().GetUInt();
|
||||
|
||||
truncate = fRequest->Stream().GetBoolean();
|
||||
|
||||
uint32 size;
|
||||
const void* ptr = fRequest->Stream().GetOpaque(&size);
|
||||
handle->fSize = size;
|
||||
memcpy(handle->fData, ptr, size);
|
||||
|
||||
return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef REQUESTINTERPRETER_H
|
||||
#define REQUESTINTERPRETER_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "FileInfo.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "RPCCallbackRequest.h"
|
||||
|
||||
|
||||
class RequestInterpreter {
|
||||
public:
|
||||
RequestInterpreter(RPC::CallbackRequest* request);
|
||||
~RequestInterpreter();
|
||||
|
||||
inline uint32 OperationCount();
|
||||
inline uint32 Operation();
|
||||
|
||||
status_t GetAttr(FileHandle* handle, int* mask);
|
||||
status_t Recall(FileHandle* handle, bool& truncate,
|
||||
uint32* stateSeq, uint32* stateID);
|
||||
|
||||
private:
|
||||
uint32 fOperationCount;
|
||||
uint32 fLastOperation;
|
||||
|
||||
RPC::CallbackRequest* fRequest;
|
||||
};
|
||||
|
||||
|
||||
inline uint32
|
||||
RequestInterpreter::OperationCount()
|
||||
{
|
||||
return fOperationCount;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
RequestInterpreter::Operation()
|
||||
{
|
||||
fLastOperation = fRequest->Stream().GetUInt();
|
||||
return fLastOperation;
|
||||
}
|
||||
|
||||
|
||||
#endif // REQUESTINTERPRETER_H
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "RootInode.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "MetadataCache.h"
|
||||
#include "Request.h"
|
||||
|
||||
|
||||
RootInode::RootInode()
|
||||
:
|
||||
fInfoCacheExpire(0),
|
||||
fName(NULL),
|
||||
fIOSize(0)
|
||||
{
|
||||
mutex_init(&fInfoCacheLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
RootInode::~RootInode()
|
||||
{
|
||||
free(const_cast<char*>(fName));
|
||||
mutex_destroy(&fInfoCacheLock);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RootInode::ReadInfo(struct fs_info* info)
|
||||
{
|
||||
ASSERT(info != NULL);
|
||||
|
||||
status_t result = _UpdateInfo();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
memcpy(info, &fInfoCache, sizeof(struct fs_info));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RootInode::_UpdateInfo(bool force)
|
||||
{
|
||||
if (!force && fInfoCacheExpire > time(NULL))
|
||||
return B_OK;
|
||||
|
||||
MutexLocker _(fInfoCacheLock);
|
||||
|
||||
if (fInfoCacheExpire > time(NULL))
|
||||
return B_OK;
|
||||
|
||||
do {
|
||||
RPC::Server* server = fFileSystem->Server();
|
||||
Request request(server, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
Attribute attr[] = { FATTR4_FILES_FREE, FATTR4_FILES_TOTAL,
|
||||
FATTR4_MAXREAD, FATTR4_MAXWRITE, FATTR4_SPACE_FREE,
|
||||
FATTR4_SPACE_TOTAL };
|
||||
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), server))
|
||||
continue;
|
||||
|
||||
reply.PutFH();
|
||||
|
||||
AttrValue* values;
|
||||
uint32 count, next = 0;
|
||||
result = reply.GetAttr(&values, &count);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_FILES_FREE) {
|
||||
fInfoCache.free_nodes = values[next].fData.fValue64;
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_FILES_TOTAL) {
|
||||
fInfoCache.total_nodes = values[next].fData.fValue64;
|
||||
next++;
|
||||
}
|
||||
|
||||
uint64 ioSize = LONGLONG_MAX;
|
||||
if (count >= next && values[next].fAttribute == FATTR4_MAXREAD) {
|
||||
ioSize = min_c(ioSize, values[next].fData.fValue64);
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_MAXWRITE) {
|
||||
ioSize = min_c(ioSize, values[next].fData.fValue64);
|
||||
next++;
|
||||
}
|
||||
|
||||
if (ioSize == LONGLONG_MAX)
|
||||
ioSize = 32768;
|
||||
fInfoCache.io_size = ioSize;
|
||||
fInfoCache.block_size = ioSize;
|
||||
fIOSize = ioSize;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_SPACE_FREE) {
|
||||
fInfoCache.free_blocks = values[next].fData.fValue64 / ioSize;
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_SPACE_TOTAL) {
|
||||
fInfoCache.total_blocks = values[next].fData.fValue64 / ioSize;
|
||||
next++;
|
||||
}
|
||||
|
||||
delete[] values;
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
fInfoCache.flags = B_FS_IS_PERSISTENT | B_FS_IS_SHARED
|
||||
| B_FS_SUPPORTS_NODE_MONITORING;
|
||||
|
||||
if (fFileSystem->NamedAttrs()
|
||||
|| fFileSystem->GetConfiguration().fEmulateNamedAttrs)
|
||||
fInfoCache.flags |= B_FS_HAS_MIME | B_FS_HAS_ATTR;
|
||||
|
||||
strncpy(fInfoCache.volume_name, fName, B_FILE_NAME_LENGTH);
|
||||
|
||||
fInfoCacheExpire = time(NULL) + MetadataCache::kExpirationTime;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
RootInode::ProbeMigration()
|
||||
{
|
||||
do {
|
||||
RPC::Server* server = fFileSystem->Server();
|
||||
Request request(server, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
req.Access();
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
continue;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
if (reply.NFS4Error() == NFS4ERR_MOVED)
|
||||
return true;
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), server))
|
||||
continue;
|
||||
|
||||
return false;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
RootInode::GetLocations(AttrValue** attrv)
|
||||
{
|
||||
ASSERT(attrv != NULL);
|
||||
|
||||
do {
|
||||
RPC::Server* server = fFileSystem->Server();
|
||||
Request request(server, fFileSystem);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fInfo.fHandle);
|
||||
Attribute attr[] = { FATTR4_FS_LOCATIONS };
|
||||
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
|
||||
|
||||
status_t result = request.Send();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
if (HandleErrors(reply.NFS4Error(), server))
|
||||
continue;
|
||||
|
||||
reply.PutFH();
|
||||
|
||||
uint32 count;
|
||||
result = reply.GetAttr(attrv, &count);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
if (count < 1)
|
||||
return B_ERROR;
|
||||
return B_OK;
|
||||
|
||||
} while (true);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
RootInode::Name() const
|
||||
{
|
||||
ASSERT(fName != NULL);
|
||||
return fName;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef ROOTINODE_H
|
||||
#define ROOTINODE_H
|
||||
|
||||
|
||||
#include <fs_info.h>
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
class RootInode : public Inode {
|
||||
public:
|
||||
RootInode();
|
||||
~RootInode();
|
||||
|
||||
virtual const char* Name() const;
|
||||
inline void SetName(const char* name);
|
||||
|
||||
status_t ReadInfo(struct fs_info* info);
|
||||
inline void MakeInfoInvalid();
|
||||
|
||||
inline uint32 IOSize();
|
||||
|
||||
bool ProbeMigration();
|
||||
status_t GetLocations(AttrValue** attr);
|
||||
|
||||
private:
|
||||
struct fs_info fInfoCache;
|
||||
mutex fInfoCacheLock;
|
||||
time_t fInfoCacheExpire;
|
||||
|
||||
const char* fName;
|
||||
|
||||
uint32 fIOSize;
|
||||
|
||||
status_t _UpdateInfo(bool force = false);
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline void
|
||||
RootInode::MakeInfoInvalid()
|
||||
{
|
||||
fInfoCacheExpire = 0;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
RootInode::IOSize()
|
||||
{
|
||||
if (fIOSize == 0)
|
||||
_UpdateInfo(true);
|
||||
return fIOSize;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
RootInode::SetName(const char* name)
|
||||
{
|
||||
ASSERT(name != NULL);
|
||||
|
||||
free(const_cast<char*>(fName));
|
||||
fName = strdup(name);
|
||||
}
|
||||
|
||||
|
||||
#endif // ROOTINODE_H
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "VnodeToInode.h"
|
||||
|
||||
|
||||
Inode*
|
||||
VnodeToInode::Get()
|
||||
{
|
||||
if (fInode == NULL) {
|
||||
status_t result = fFileSystem->GetInode(fID, &fInode);
|
||||
if (result != B_OK)
|
||||
fInode = NULL;
|
||||
}
|
||||
|
||||
return fInode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VnodeToInode::Replace(Inode* newInode)
|
||||
{
|
||||
WriteLocker _(fLock);
|
||||
if (fInode != NULL && !IsRoot()) {
|
||||
fInode->GetFileSystem()->InoIdMap()->MarkRemoved(fID);
|
||||
delete fInode;
|
||||
}
|
||||
|
||||
fInode = newInode;
|
||||
if (fInode != NULL) {
|
||||
ASSERT(fFileSystem == fInode->GetFileSystem());
|
||||
fInode->GetFileSystem()->InoIdMap()->AddEntry(fInode->fInfo, fID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef VNODETOINODE_H
|
||||
#define VNODETOINODE_H
|
||||
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "Inode.h"
|
||||
#include "InodeIdMap.h"
|
||||
#include "RootInode.h"
|
||||
|
||||
class VnodeToInode {
|
||||
public:
|
||||
inline VnodeToInode(ino_t id, FileSystem* fileSystem);
|
||||
inline ~VnodeToInode();
|
||||
|
||||
inline void Lock();
|
||||
inline void Unlock();
|
||||
|
||||
inline Inode* GetPointer() const;
|
||||
Inode* Get();
|
||||
void Replace(Inode* newInode);
|
||||
|
||||
inline void Remove();
|
||||
inline void Clear();
|
||||
|
||||
inline ino_t ID() const;
|
||||
|
||||
inline bool IsRoot() const;
|
||||
private:
|
||||
ino_t fID;
|
||||
rw_lock fLock;
|
||||
|
||||
Inode* fInode;
|
||||
FileSystem* fFileSystem;
|
||||
};
|
||||
|
||||
class VnodeToInodeLocking {
|
||||
public:
|
||||
inline bool Lock(VnodeToInode* vti)
|
||||
{
|
||||
vti->Lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void Unlock(VnodeToInode* vti)
|
||||
{
|
||||
vti->Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
typedef AutoLocker<VnodeToInode, VnodeToInodeLocking> VnodeToInodeLocker;
|
||||
|
||||
inline
|
||||
VnodeToInode::VnodeToInode(ino_t id, FileSystem* fileSystem)
|
||||
:
|
||||
fID(id),
|
||||
fInode(NULL),
|
||||
fFileSystem(fileSystem)
|
||||
{
|
||||
rw_lock_init(&fLock, NULL);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
VnodeToInode::~VnodeToInode()
|
||||
{
|
||||
Remove();
|
||||
if (fFileSystem != NULL && !IsRoot())
|
||||
fFileSystem->InoIdMap()->RemoveEntry(fID);
|
||||
rw_lock_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
VnodeToInode::Lock()
|
||||
{
|
||||
rw_lock_read_lock(&fLock);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
VnodeToInode::Unlock()
|
||||
{
|
||||
rw_lock_read_unlock(&fLock);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
VnodeToInode::Remove()
|
||||
{
|
||||
Replace(NULL);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
VnodeToInode::Clear()
|
||||
{
|
||||
WriteLocker _(fLock);
|
||||
if (!IsRoot())
|
||||
delete fInode;
|
||||
fInode = NULL;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
VnodeToInode::IsRoot() const
|
||||
{
|
||||
return fInode && fFileSystem && fInode->ID() == fFileSystem->Root()->ID();
|
||||
}
|
||||
|
||||
|
||||
inline Inode*
|
||||
VnodeToInode::GetPointer() const
|
||||
{
|
||||
return fInode;
|
||||
}
|
||||
|
||||
|
||||
inline ino_t
|
||||
VnodeToInode::ID() const
|
||||
{
|
||||
return fID;
|
||||
}
|
||||
|
||||
#endif // VNODETOINODE_H
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "WorkQueue.h"
|
||||
|
||||
#include <io_requests.h>
|
||||
|
||||
|
||||
WorkQueue* gWorkQueue = NULL;
|
||||
|
||||
|
||||
WorkQueue::WorkQueue()
|
||||
:
|
||||
fQueueSemaphore(create_sem(0, NULL)),
|
||||
fThreadCancel(create_sem(0, NULL))
|
||||
{
|
||||
mutex_init(&fQueueLock, NULL);
|
||||
|
||||
fThread = spawn_kernel_thread(&WorkQueue::LaunchWorkingThread,
|
||||
"NFSv4 Work Queue", B_NORMAL_PRIORITY, this);
|
||||
if (fThread < B_OK) {
|
||||
fInitError = fThread;
|
||||
return;
|
||||
}
|
||||
|
||||
status_t result = resume_thread(fThread);
|
||||
if (result != B_OK) {
|
||||
kill_thread(fThread);
|
||||
fInitError = result;
|
||||
return;
|
||||
}
|
||||
|
||||
fInitError = B_OK;
|
||||
}
|
||||
|
||||
|
||||
WorkQueue::~WorkQueue()
|
||||
{
|
||||
release_sem(fThreadCancel);
|
||||
|
||||
status_t result;
|
||||
wait_for_thread(fThread, &result);
|
||||
|
||||
mutex_destroy(&fQueueLock);
|
||||
delete_sem(fThreadCancel);
|
||||
delete_sem(fQueueSemaphore);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WorkQueue::EnqueueJob(JobType type, void* args)
|
||||
{
|
||||
WorkQueueEntry* entry = new(std::nothrow) WorkQueueEntry;
|
||||
if (entry == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
entry->fType = type;
|
||||
entry->fArguments = args;
|
||||
if (type == IORequest)
|
||||
reinterpret_cast<IORequestArgs*>(args)->fInode->BeginAIOOp();
|
||||
|
||||
MutexLocker locker(fQueueLock);
|
||||
fQueue.InsertAfter(fQueue.Tail(), entry);
|
||||
locker.Unlock();
|
||||
|
||||
release_sem(fQueueSemaphore);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WorkQueue::LaunchWorkingThread(void* object)
|
||||
{
|
||||
ASSERT(object != NULL);
|
||||
|
||||
WorkQueue* queue = reinterpret_cast<WorkQueue*>(object);
|
||||
return queue->WorkingThread();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WorkQueue::WorkingThread()
|
||||
{
|
||||
while (true) {
|
||||
object_wait_info object[2];
|
||||
object[0].object = fThreadCancel;
|
||||
object[0].type = B_OBJECT_TYPE_SEMAPHORE;
|
||||
object[0].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
|
||||
object[1].object = fQueueSemaphore;
|
||||
object[1].type = B_OBJECT_TYPE_SEMAPHORE;
|
||||
object[1].events = B_EVENT_ACQUIRE_SEMAPHORE;
|
||||
|
||||
status_t result = wait_for_objects(object, 2);
|
||||
|
||||
if (result < B_OK
|
||||
|| (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) {
|
||||
return result;
|
||||
} else if ((object[1].events & B_EVENT_ACQUIRE_SEMAPHORE) == 0)
|
||||
continue;
|
||||
|
||||
acquire_sem(fQueueSemaphore);
|
||||
|
||||
DequeueJob();
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WorkQueue::DequeueJob()
|
||||
{
|
||||
MutexLocker locker(fQueueLock);
|
||||
WorkQueueEntry* entry = fQueue.RemoveHead();
|
||||
locker.Unlock();
|
||||
ASSERT(entry != NULL);
|
||||
|
||||
void* args = entry->fArguments;
|
||||
switch (entry->fType) {
|
||||
case DelegationRecall:
|
||||
JobRecall(reinterpret_cast<DelegationRecallArgs*>(args));
|
||||
break;
|
||||
case IORequest:
|
||||
JobIO(reinterpret_cast<IORequestArgs*>(args));
|
||||
break;
|
||||
}
|
||||
|
||||
delete entry;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WorkQueue::JobRecall(DelegationRecallArgs* args)
|
||||
{
|
||||
ASSERT(args != NULL);
|
||||
args->fDelegation->GetInode()->RecallDelegation(args->fTruncate);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WorkQueue::JobIO(IORequestArgs* args)
|
||||
{
|
||||
ASSERT(args != NULL);
|
||||
|
||||
uint64 offset = io_request_offset(args->fRequest);
|
||||
uint64 length = io_request_length(args->fRequest);
|
||||
|
||||
char* buffer = reinterpret_cast<char*>(malloc(length));
|
||||
if (buffer == NULL) {
|
||||
notify_io_request(args->fRequest, B_NO_MEMORY);
|
||||
args->fInode->EndAIOOp();
|
||||
return;
|
||||
}
|
||||
|
||||
bool eof = false;
|
||||
uint64 size = 0;
|
||||
status_t result;
|
||||
if (io_request_is_write(args->fRequest)) {
|
||||
if (offset + length > args->fInode->MaxFileSize())
|
||||
length = args->fInode->MaxFileSize() - offset;
|
||||
|
||||
result = read_from_io_request(args->fRequest, buffer, length);
|
||||
do {
|
||||
size_t bytesWritten = length - size;
|
||||
result = args->fInode->WriteDirect(NULL, offset + size,
|
||||
buffer + size, &bytesWritten);
|
||||
size += bytesWritten;
|
||||
} while (size < length && result == B_OK);
|
||||
} else {
|
||||
do {
|
||||
size_t bytesRead = length - size;
|
||||
result = args->fInode->ReadDirect(NULL, offset + size, buffer,
|
||||
&bytesRead, &eof);
|
||||
if (result != B_OK)
|
||||
break;
|
||||
|
||||
result = write_to_io_request(args->fRequest, buffer, bytesRead);
|
||||
if (result != B_OK)
|
||||
break;
|
||||
|
||||
size += bytesRead;
|
||||
} while (size < length && result == B_OK && !eof);
|
||||
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
notify_io_request(args->fRequest, result);
|
||||
args->fInode->EndAIOOp();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef WORKQUEUE_H
|
||||
#define WORKQUEUE_H
|
||||
|
||||
|
||||
#include <io_requests.h>
|
||||
#include <lock.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#include "Delegation.h"
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
enum JobType {
|
||||
DelegationRecall,
|
||||
IORequest
|
||||
};
|
||||
|
||||
struct DelegationRecallArgs {
|
||||
Delegation* fDelegation;
|
||||
bool fTruncate;
|
||||
};
|
||||
|
||||
struct IORequestArgs {
|
||||
io_request* fRequest;
|
||||
Inode* fInode;
|
||||
};
|
||||
|
||||
struct WorkQueueEntry : public DoublyLinkedListLinkImpl<WorkQueueEntry> {
|
||||
JobType fType;
|
||||
void* fArguments;
|
||||
};
|
||||
|
||||
class WorkQueue {
|
||||
public:
|
||||
WorkQueue();
|
||||
~WorkQueue();
|
||||
|
||||
inline status_t InitStatus();
|
||||
|
||||
status_t EnqueueJob(JobType type, void* args);
|
||||
|
||||
protected:
|
||||
static status_t LaunchWorkingThread(void* object);
|
||||
status_t WorkingThread();
|
||||
|
||||
void DequeueJob();
|
||||
|
||||
void JobRecall(DelegationRecallArgs* args);
|
||||
void JobIO(IORequestArgs* args);
|
||||
|
||||
private:
|
||||
status_t fInitError;
|
||||
|
||||
sem_id fQueueSemaphore;
|
||||
mutex fQueueLock;
|
||||
DoublyLinkedList<WorkQueueEntry> fQueue;
|
||||
|
||||
sem_id fThreadCancel;
|
||||
thread_id fThread;
|
||||
};
|
||||
|
||||
|
||||
inline status_t
|
||||
WorkQueue::InitStatus()
|
||||
{
|
||||
return fInitError;
|
||||
}
|
||||
|
||||
|
||||
extern WorkQueue* gWorkQueue;
|
||||
|
||||
|
||||
#endif // WORKQUEUE_H
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "XDR.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
|
||||
|
||||
using namespace XDR;
|
||||
|
||||
Stream::Stream(void* buffer, uint32 size)
|
||||
:
|
||||
fBuffer(reinterpret_cast<uint32*>(buffer)),
|
||||
fSize(size),
|
||||
fPosition(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Stream::~Stream()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
Stream::_PositionToSize() const
|
||||
{
|
||||
return fPosition * sizeof(uint32);
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
Stream::_RealSize(uint32 size) const
|
||||
{
|
||||
uint32 real_size = size;
|
||||
if (real_size % 4 != 0)
|
||||
real_size = ((real_size >> 2) + 1) << 2;
|
||||
return real_size;
|
||||
}
|
||||
|
||||
|
||||
ReadStream::ReadStream(void* buffer, uint32 size)
|
||||
:
|
||||
Stream(buffer, size),
|
||||
fEOF(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ReadStream::~ReadStream()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
ReadStream::GetInt()
|
||||
{
|
||||
if (_PositionToSize() >= fSize) {
|
||||
fEOF = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return B_BENDIAN_TO_HOST_INT32(fBuffer[fPosition++]);
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
ReadStream::GetUInt()
|
||||
{
|
||||
if (_PositionToSize() >= fSize) {
|
||||
fEOF = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return B_BENDIAN_TO_HOST_INT32(fBuffer[fPosition++]);
|
||||
}
|
||||
|
||||
|
||||
int64
|
||||
ReadStream::GetHyper()
|
||||
{
|
||||
if (_PositionToSize() + sizeof(int64) > fSize) {
|
||||
fEOF = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64* ptr = reinterpret_cast<int64*>(fBuffer + fPosition);
|
||||
fPosition += 2;
|
||||
|
||||
return B_BENDIAN_TO_HOST_INT64(*ptr);
|
||||
}
|
||||
|
||||
|
||||
uint64
|
||||
ReadStream::GetUHyper()
|
||||
{
|
||||
if (_PositionToSize() + sizeof(uint64) > fSize) {
|
||||
fEOF = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64* ptr = reinterpret_cast<uint64*>(fBuffer + fPosition);
|
||||
fPosition += 2;
|
||||
|
||||
return B_BENDIAN_TO_HOST_INT64(*ptr);
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
ReadStream::GetString()
|
||||
{
|
||||
if (_PositionToSize() >= fSize) {
|
||||
fEOF = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32 size;
|
||||
const void* ptr = GetOpaque(&size);
|
||||
if (ptr == NULL)
|
||||
return NULL;
|
||||
|
||||
char* str = reinterpret_cast<char*>(malloc(size + 1));
|
||||
if (str == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(str, ptr, size);
|
||||
str[size] = 0;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
const void*
|
||||
ReadStream::GetOpaque(uint32* size)
|
||||
{
|
||||
if (_PositionToSize() >= fSize) {
|
||||
fEOF = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* ptr = NULL;
|
||||
uint32 s = GetUInt();
|
||||
if (s != 0) {
|
||||
ptr = fBuffer + fPosition;
|
||||
if (_PositionToSize() + s <= fSize)
|
||||
fPosition += _RealSize(s) / sizeof(uint32);
|
||||
else {
|
||||
s = fSize - _PositionToSize();
|
||||
fPosition = fSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (size != NULL)
|
||||
*size = s;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
WriteStream::WriteStream()
|
||||
:
|
||||
Stream(malloc(kInitialSize), kInitialSize),
|
||||
fError(B_OK)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WriteStream::WriteStream(const WriteStream& x)
|
||||
:
|
||||
Stream(malloc(x.fSize), x.fSize),
|
||||
fError(x.fError)
|
||||
{
|
||||
fPosition = x.fPosition;
|
||||
memcpy(fBuffer, x.fBuffer, fSize);
|
||||
}
|
||||
|
||||
|
||||
WriteStream::~WriteStream()
|
||||
{
|
||||
free(fBuffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WriteStream::Clear()
|
||||
{
|
||||
free(fBuffer);
|
||||
fSize = kInitialSize;
|
||||
fBuffer = reinterpret_cast<uint32*>(malloc(fSize));
|
||||
fError = B_OK;
|
||||
fPosition = 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::InsertUInt(Stream::Position pos, uint32 x)
|
||||
{
|
||||
if (pos * sizeof(uint32) >= fSize) {
|
||||
fError = B_BAD_VALUE;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
fBuffer[pos] = B_HOST_TO_BENDIAN_INT32(x);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::AddInt(int32 x)
|
||||
{
|
||||
status_t err = _CheckResize(sizeof(int32));
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
fBuffer[fPosition++] = B_HOST_TO_BENDIAN_INT32(x);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::AddUInt(uint32 x)
|
||||
{
|
||||
status_t err = _CheckResize(sizeof(uint32));
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
fBuffer[fPosition++] = B_HOST_TO_BENDIAN_INT32(x);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::AddHyper(int64 x)
|
||||
{
|
||||
status_t err = _CheckResize(sizeof(int64));
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
int64* ptr = reinterpret_cast<int64*>(fBuffer + fPosition);
|
||||
*ptr = B_HOST_TO_BENDIAN_INT64(x);
|
||||
fPosition += 2;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::AddUHyper(uint64 x)
|
||||
{
|
||||
status_t err = _CheckResize(sizeof(uint64));
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
uint64* ptr = reinterpret_cast<uint64*>(fBuffer + fPosition);
|
||||
*ptr = B_HOST_TO_BENDIAN_INT64(x);
|
||||
fPosition += 2;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::AddString(const char* str, uint32 maxlen)
|
||||
{
|
||||
uint32 len = strlen(str);
|
||||
uint32 size = maxlen == 0 ? len : min_c(maxlen, len);
|
||||
|
||||
return AddOpaque(str, size);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::AddOpaque(const void* ptr, uint32 size)
|
||||
{
|
||||
uint32 real_size = _RealSize(size);
|
||||
status_t err = _CheckResize(real_size + sizeof(uint32));
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
AddUInt(size);
|
||||
memset(fBuffer + fPosition, 0, real_size);
|
||||
memcpy(fBuffer + fPosition, ptr, size);
|
||||
fPosition += real_size / sizeof(int32);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::AddOpaque(const WriteStream& stream)
|
||||
{
|
||||
return AddOpaque(stream.Buffer(), stream.Size());
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::Append(const WriteStream& stream)
|
||||
{
|
||||
uint32 size = stream.Size();
|
||||
status_t err = _CheckResize(size);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
memcpy(fBuffer + fPosition, stream.Buffer(), size);
|
||||
fPosition += size / sizeof(int32);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
WriteStream::_CheckResize(uint32 size)
|
||||
{
|
||||
if (_PositionToSize() + size <= fSize)
|
||||
return B_OK;
|
||||
|
||||
uint32 new_size = max_c(fSize * 2, fPosition * sizeof(uint32) + size);
|
||||
|
||||
void* ptr = realloc(fBuffer, new_size);
|
||||
if (ptr == NULL) {
|
||||
fError = B_NO_MEMORY;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
fBuffer = reinterpret_cast<uint32*>(ptr);
|
||||
fSize = new_size;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef XDR_H
|
||||
#define XDR_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
namespace XDR {
|
||||
|
||||
class Stream {
|
||||
public:
|
||||
typedef uint32 Position;
|
||||
|
||||
virtual ~Stream();
|
||||
|
||||
inline const void* Buffer() const;
|
||||
inline Position Current() const;
|
||||
|
||||
protected:
|
||||
Stream(void* buffer, uint32 size);
|
||||
|
||||
inline uint32 _PositionToSize() const;
|
||||
inline uint32 _RealSize(uint32 size) const;
|
||||
|
||||
uint32* fBuffer;
|
||||
uint32 fSize;
|
||||
Position fPosition;
|
||||
};
|
||||
|
||||
class ReadStream : public Stream {
|
||||
public:
|
||||
ReadStream(void* buffer, uint32 size);
|
||||
virtual ~ReadStream();
|
||||
|
||||
inline void SetPosition(Position position);
|
||||
|
||||
inline int Size() const;
|
||||
|
||||
int32 GetInt();
|
||||
uint32 GetUInt();
|
||||
int64 GetHyper();
|
||||
uint64 GetUHyper();
|
||||
|
||||
inline bool GetBoolean();
|
||||
|
||||
char* GetString();
|
||||
|
||||
const void* GetOpaque(uint32* size);
|
||||
|
||||
inline bool IsEOF() const;
|
||||
|
||||
private:
|
||||
bool fEOF;
|
||||
};
|
||||
|
||||
class WriteStream : public Stream {
|
||||
public:
|
||||
WriteStream();
|
||||
WriteStream(const WriteStream& x);
|
||||
virtual ~WriteStream();
|
||||
|
||||
inline int Size() const;
|
||||
void Clear();
|
||||
|
||||
status_t InsertUInt(Stream::Position pos, uint32 x);
|
||||
|
||||
status_t AddInt(int32 x);
|
||||
status_t AddUInt(uint32 x);
|
||||
status_t AddHyper(int64 x);
|
||||
status_t AddUHyper(uint64 x);
|
||||
|
||||
inline status_t AddBoolean(bool x);
|
||||
|
||||
status_t AddString(const char* str, uint32 maxlen = 0);
|
||||
|
||||
status_t AddOpaque(const void* ptr, uint32 size);
|
||||
status_t AddOpaque(const WriteStream& stream);
|
||||
|
||||
status_t Append(const WriteStream& stream);
|
||||
|
||||
inline status_t Error() const;
|
||||
|
||||
private:
|
||||
status_t _CheckResize(uint32 size);
|
||||
|
||||
status_t fError;
|
||||
|
||||
static const uint32 kInitialSize = 64;
|
||||
};
|
||||
|
||||
|
||||
inline const void*
|
||||
Stream::Buffer() const
|
||||
{
|
||||
return fBuffer;
|
||||
}
|
||||
|
||||
|
||||
inline Stream::Position
|
||||
Stream::Current() const
|
||||
{
|
||||
return fPosition;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
ReadStream::SetPosition(Position position)
|
||||
{
|
||||
fPosition = position;
|
||||
}
|
||||
|
||||
|
||||
inline int
|
||||
ReadStream::Size() const
|
||||
{
|
||||
return fSize;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
ReadStream::IsEOF() const
|
||||
{
|
||||
return fEOF;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
ReadStream::GetBoolean()
|
||||
{
|
||||
return GetInt() != 0;
|
||||
}
|
||||
|
||||
|
||||
inline int
|
||||
WriteStream::Size() const
|
||||
{
|
||||
return fPosition * sizeof(uint32);
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
WriteStream::AddBoolean(bool x)
|
||||
{
|
||||
return AddInt(static_cast<int32>(x));
|
||||
}
|
||||
|
||||
|
||||
inline status_t
|
||||
WriteStream::Error() const
|
||||
{
|
||||
return fError;
|
||||
}
|
||||
|
||||
|
||||
} // namespace XDR
|
||||
|
||||
|
||||
#endif // XDR_H
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include "IdMapper.h"
|
||||
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <File.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <OS.h>
|
||||
#include <Path.h>
|
||||
|
||||
|
||||
port_id gRequestPort;
|
||||
port_id gReplyPort;
|
||||
|
||||
const char* kNobodyName = "nobody";
|
||||
uid_t gNobodyId;
|
||||
|
||||
const char* kNogroupName = "nobody";
|
||||
uid_t gNogroupId;
|
||||
|
||||
const char* gDomainName = "localdomain";
|
||||
|
||||
|
||||
status_t
|
||||
SendError(status_t error)
|
||||
{
|
||||
return write_port(gReplyPort, MsgError, &error, sizeof(error));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MatchDomain(char* name)
|
||||
{
|
||||
char* domain = strchr(name, '@');
|
||||
if (domain == NULL)
|
||||
return B_MISMATCHED_VALUES;
|
||||
|
||||
if (strcmp(domain + 1, gDomainName) != 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*domain = '\0';
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
AddDomain(const char* name)
|
||||
{
|
||||
uint32 fullLength = strlen(name) + strlen(gDomainName) + 2;
|
||||
char* fullName = reinterpret_cast<char*>(malloc(fullLength));
|
||||
if (fullName == NULL)
|
||||
return NULL;
|
||||
|
||||
strcpy(fullName, name);
|
||||
strcat(fullName, "@");
|
||||
strcat(fullName, gDomainName);
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NameToUID(void* buffer)
|
||||
{
|
||||
char* userName = reinterpret_cast<char*>(buffer);
|
||||
|
||||
struct passwd* userInfo = NULL;
|
||||
|
||||
if (MatchDomain(userName) == B_OK)
|
||||
userInfo = getpwnam(userName);
|
||||
|
||||
if (userInfo == NULL)
|
||||
return write_port(gReplyPort, MsgReply, &gNobodyId, sizeof(gNobodyId));
|
||||
|
||||
return write_port(gReplyPort, MsgReply, &userInfo->pw_uid, sizeof(uid_t));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UIDToName(void* buffer)
|
||||
{
|
||||
uid_t userId = *reinterpret_cast<uid_t*>(buffer);
|
||||
|
||||
const char* name = NULL;
|
||||
|
||||
struct passwd* userInfo = getpwuid(userId);
|
||||
if (userInfo != NULL) {
|
||||
name = userInfo->pw_name;
|
||||
name = AddDomain(name);
|
||||
}
|
||||
|
||||
status_t result;
|
||||
|
||||
if (name != NULL) {
|
||||
result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1);
|
||||
free(const_cast<char*>(name));
|
||||
} else {
|
||||
result = write_port(gReplyPort, MsgReply, kNobodyName,
|
||||
strlen(kNobodyName) + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NameToGID(void* buffer)
|
||||
{
|
||||
char* groupName = reinterpret_cast<char*>(buffer);
|
||||
|
||||
struct group* groupInfo = NULL;
|
||||
|
||||
if (MatchDomain(groupName) == B_OK)
|
||||
groupInfo = getgrnam(groupName);
|
||||
|
||||
if (groupInfo == NULL) {
|
||||
return write_port(gReplyPort, MsgReply, &gNogroupId,
|
||||
sizeof(gNogroupId));
|
||||
}
|
||||
|
||||
return write_port(gReplyPort, MsgReply, &groupInfo->gr_gid, sizeof(gid_t));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
GIDToName(void* buffer)
|
||||
{
|
||||
gid_t groupId = *reinterpret_cast<gid_t*>(buffer);
|
||||
|
||||
const char* name = NULL;
|
||||
|
||||
struct group* groupInfo = getgrgid(groupId);
|
||||
if (groupInfo != NULL) {
|
||||
name = groupInfo->gr_name;
|
||||
name = AddDomain(name);
|
||||
}
|
||||
|
||||
status_t result;
|
||||
|
||||
if (name != NULL) {
|
||||
result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1);
|
||||
free(const_cast<char*>(name));
|
||||
} else {
|
||||
result = write_port(gReplyPort, MsgReply, kNogroupName,
|
||||
strlen(kNogroupName) + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ParseRequest(int32 code, void* buffer)
|
||||
{
|
||||
switch (code) {
|
||||
case MsgNameToUID:
|
||||
return NameToUID(buffer);
|
||||
|
||||
case MsgUIDToName:
|
||||
return UIDToName(buffer);
|
||||
|
||||
case MsgNameToGID:
|
||||
return NameToGID(buffer);
|
||||
|
||||
case MsgGIDToName:
|
||||
return GIDToName(buffer);
|
||||
|
||||
default:
|
||||
return SendError(B_BAD_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MainLoop()
|
||||
{
|
||||
do {
|
||||
ssize_t size = port_buffer_size(gRequestPort);
|
||||
if (size < B_OK)
|
||||
return 0;
|
||||
|
||||
void* buffer = malloc(size);
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
int32 code;
|
||||
size = read_port(gRequestPort, &code, buffer, size);
|
||||
if (size < B_OK) {
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
status_t result = ParseRequest(code, buffer);
|
||||
free(buffer);
|
||||
|
||||
if (result != B_OK)
|
||||
return 0;
|
||||
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReadSettings()
|
||||
{
|
||||
BPath path;
|
||||
status_t result = find_directory(B_COMMON_SETTINGS_DIRECTORY, &path);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
result = path.Append("nfs4_idmapper.conf");
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
BFile file(path.Path(), B_READ_ONLY);
|
||||
if (file.InitCheck() != B_OK)
|
||||
return file.InitCheck();
|
||||
|
||||
off_t size;
|
||||
result = file.GetSize(&size);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
void* buffer = malloc(size);
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
file.Read(buffer, size);
|
||||
|
||||
gDomainName = reinterpret_cast<char*>(buffer);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
gRequestPort = find_port(kRequestPortName);
|
||||
if (gRequestPort < B_OK) {
|
||||
fprintf(stderr, "%s\n", strerror(gRequestPort));
|
||||
return gRequestPort;
|
||||
}
|
||||
|
||||
gReplyPort = find_port(kReplyPortName);
|
||||
if (gReplyPort < B_OK) {
|
||||
fprintf(stderr, "%s\n", strerror(gReplyPort));
|
||||
return gReplyPort;
|
||||
}
|
||||
|
||||
ReadSettings();
|
||||
|
||||
struct passwd* userInfo = getpwnam(kNobodyName);
|
||||
if (userInfo != NULL)
|
||||
gNobodyId = userInfo->pw_uid;
|
||||
else
|
||||
gNobodyId = 0;
|
||||
|
||||
struct group* groupInfo = getgrnam(kNogroupName);
|
||||
if (groupInfo != NULL)
|
||||
gNogroupId = groupInfo->gr_gid;
|
||||
else
|
||||
gNogroupId = 0;
|
||||
|
||||
return MainLoop();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef IDMAPPER_H
|
||||
#define IDMAPPER_H
|
||||
|
||||
|
||||
enum MsgCode {
|
||||
MsgError,
|
||||
MsgReply,
|
||||
MsgNameToUID,
|
||||
MsgUIDToName,
|
||||
MsgNameToGID,
|
||||
MsgGIDToName
|
||||
};
|
||||
|
||||
static const char* kRequestPortName = "nfs4idmap_request";
|
||||
static const char* kReplyPortName = "nfs4idmap_reply";
|
||||
|
||||
|
||||
#endif // IDMAPPER_H
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;
|
||||
|
||||
Application nfs4_idmapper_server
|
||||
:
|
||||
IdMapper.cpp
|
||||
|
||||
:
|
||||
be
|
||||
$(TARGET_LIBSUPC++)
|
||||
|
||||
:
|
||||
nfs4_idmapper_server.rdef
|
||||
;
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* nfs4_idmapper_server.rdef
|
||||
*/
|
||||
|
||||
resource app_signature "application/x-vnd.Haiku-nfs4_idmapper-server";
|
||||
|
||||
resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
|
||||
|
||||
resource app_version {
|
||||
major = 1,
|
||||
middle = 0,
|
||||
minor = 0,
|
||||
|
||||
variety = B_APPV_ALPHA,
|
||||
internal = 0,
|
||||
|
||||
short_info = "nfs4_idmapper_server",
|
||||
long_info = "nfs4_idmapper_server ©2012 Haiku, Inc"
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel network ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network devices ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network dns_resolver ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network notifications ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network protocols ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network stack ;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
#ifndef DEFINITIONS_H
|
||||
#define DEFINITIONS_H
|
||||
|
||||
|
||||
const char* kPortNameReq = "dns_resolver_req";
|
||||
const char* kPortNameRpl = "dns_resolver_rpl";
|
||||
|
||||
enum MsgCodes {
|
||||
MsgReply,
|
||||
MsgError,
|
||||
MsgGetAddrInfo,
|
||||
};
|
||||
|
||||
|
||||
#endif // DEFINITIONS_H
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel network dns_resolver ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel network dns_resolver kernel_add_on ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network dns_resolver server ;
|
||||
@@ -0,0 +1,9 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel network dns_resolver kernel_add_on ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||
|
||||
KernelAddon dns_resolver :
|
||||
dns_resolver.cpp
|
||||
;
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include <net/dns_resolver.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <lock.h>
|
||||
#include <port.h>
|
||||
#include <team.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "Definitions.h"
|
||||
|
||||
|
||||
static mutex gPortLock;
|
||||
static port_id gPortRequest = -1;
|
||||
static port_id gPortReply = -1;
|
||||
|
||||
static const int32 kQueueLength = 1;
|
||||
|
||||
|
||||
static status_t
|
||||
dns_resolver_repair()
|
||||
{
|
||||
status_t result = B_OK;
|
||||
|
||||
gPortRequest = create_port(kQueueLength, kPortNameReq);
|
||||
if (gPortRequest < B_OK)
|
||||
return gPortRequest;
|
||||
|
||||
gPortReply = create_port(kQueueLength, kPortNameRpl);
|
||||
if (gPortReply < B_OK) {
|
||||
delete_port(gPortRequest);
|
||||
return gPortReply;
|
||||
}
|
||||
|
||||
char path[256];
|
||||
if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast<dev_t>(-1),
|
||||
false, path, sizeof(path)) != B_OK) {
|
||||
delete_port(gPortReply);
|
||||
delete_port(gPortRequest);
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
strlcat(path, "/dns_resolver_server", sizeof(path));
|
||||
|
||||
const char* args[] = { path, NULL };
|
||||
thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY,
|
||||
B_SYSTEM_TEAM, 0);
|
||||
if (thread < B_OK) {
|
||||
delete_port(gPortReply);
|
||||
delete_port(gPortRequest);
|
||||
return thread;
|
||||
}
|
||||
|
||||
set_port_owner(gPortRequest, thread);
|
||||
set_port_owner(gPortReply, thread);
|
||||
|
||||
result = resume_thread(thread);
|
||||
if (result != B_OK) {
|
||||
kill_thread(thread);
|
||||
delete_port(gPortReply);
|
||||
delete_port(gPortRequest);
|
||||
return result;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
dns_resolver_init()
|
||||
{
|
||||
mutex_init(&gPortLock, NULL);
|
||||
return dns_resolver_repair();
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
dns_resolver_uninit()
|
||||
{
|
||||
delete_port(gPortRequest);
|
||||
delete_port(gPortReply);
|
||||
mutex_destroy(&gPortLock);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
RelocateEntries(struct addrinfo* addr)
|
||||
{
|
||||
char* generalOffset = reinterpret_cast<char*>(addr);
|
||||
|
||||
struct addrinfo* current = addr;
|
||||
while (current != NULL) {
|
||||
uint64 addrOffset = reinterpret_cast<uint64>(current->ai_addr);
|
||||
uint64 nameOffset = reinterpret_cast<uint64>(current->ai_canonname);
|
||||
uint64 nextOffset = reinterpret_cast<uint64>(current->ai_next);
|
||||
|
||||
if (current->ai_addr != NULL) {
|
||||
current->ai_addr
|
||||
= reinterpret_cast<sockaddr*>(generalOffset + addrOffset);
|
||||
}
|
||||
|
||||
if (current->ai_canonname != NULL)
|
||||
current->ai_canonname = generalOffset + nameOffset;
|
||||
|
||||
if (current->ai_next != NULL) {
|
||||
current->ai_next
|
||||
= reinterpret_cast<addrinfo*>(generalOffset + nextOffset);
|
||||
}
|
||||
|
||||
current = current->ai_next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
GetAddrInfo(const char* node, const char* service,
|
||||
const struct addrinfo* hints, struct addrinfo** res)
|
||||
{
|
||||
uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1;
|
||||
uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1;
|
||||
uint32 size = nodeSize + serviceSize + sizeof(*hints);
|
||||
char* buffer = reinterpret_cast<char*>(malloc(size));
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
MemoryDeleter _(buffer);
|
||||
|
||||
off_t off = 0;
|
||||
if (node != NULL)
|
||||
strcpy(buffer + off, node);
|
||||
else
|
||||
buffer[off] = '\0';
|
||||
off += nodeSize;
|
||||
|
||||
if (service != NULL)
|
||||
strcpy(buffer + off, service);
|
||||
else
|
||||
buffer[off] = '\0';
|
||||
off += serviceSize;
|
||||
|
||||
if (hints != NULL)
|
||||
memcpy(buffer + off, hints, sizeof(*hints));
|
||||
else {
|
||||
struct addrinfo *nullHints
|
||||
= reinterpret_cast<struct addrinfo*>(buffer + off);
|
||||
memset(nullHints, 0, sizeof(*nullHints));
|
||||
nullHints->ai_family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
MutexLocker locker(gPortLock);
|
||||
do {
|
||||
status_t result = write_port(gPortRequest, MsgGetAddrInfo, buffer,
|
||||
size);
|
||||
if (result != B_OK) {
|
||||
result = dns_resolver_repair();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t replySize = port_buffer_size(gPortReply);
|
||||
if (replySize < B_OK) {
|
||||
result = dns_resolver_repair();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
continue;
|
||||
}
|
||||
|
||||
void* reply = malloc(replySize);
|
||||
if (reply == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
int32 code;
|
||||
replySize = read_port(gPortReply, &code, reply, replySize);
|
||||
if (replySize < B_OK) {
|
||||
result = dns_resolver_repair();
|
||||
if (result != B_OK) {
|
||||
free(reply);
|
||||
return result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
struct addrinfo* addr;
|
||||
switch (code) {
|
||||
case MsgReply:
|
||||
addr = reinterpret_cast<struct addrinfo*>(reply);
|
||||
RelocateEntries(addr);
|
||||
*res = addr;
|
||||
return B_OK;
|
||||
case MsgError:
|
||||
result = *reinterpret_cast<status_t*>(reply);
|
||||
free(reply);
|
||||
return result;
|
||||
default:
|
||||
free(reply);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
dns_resolver_std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
return dns_resolver_init();
|
||||
case B_MODULE_UNINIT:
|
||||
return dns_resolver_uninit();
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static dns_resolver_module sDNSResolverModule = {
|
||||
{
|
||||
DNS_RESOLVER_MODULE_NAME,
|
||||
0,
|
||||
dns_resolver_std_ops,
|
||||
},
|
||||
|
||||
GetAddrInfo,
|
||||
};
|
||||
|
||||
module_info* modules[] = {
|
||||
(module_info*)&sDNSResolverModule,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel network dns_resolver server ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||
|
||||
Application dns_resolver_server
|
||||
:
|
||||
main.cpp
|
||||
|
||||
:
|
||||
be $(TARGET_NETWORK_LIBS)
|
||||
|
||||
:
|
||||
dns_resolver_server.rdef
|
||||
;
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* dns_resolver_server.rdef
|
||||
*/
|
||||
|
||||
resource app_signature "application/x-vnd.Haiku-dns-resolver-server";
|
||||
|
||||
resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
|
||||
|
||||
resource app_version {
|
||||
major = 1,
|
||||
middle = 0,
|
||||
minor = 0,
|
||||
|
||||
variety = B_APPV_ALPHA,
|
||||
internal = 0,
|
||||
|
||||
short_info = "dns_resolver_server",
|
||||
long_info = "dns_resolver_server ©2012 Haiku, Inc"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2012 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Paweł Dziepak, pdziepak@quarnos.org
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <OS.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "Definitions.h"
|
||||
|
||||
|
||||
port_id gRequestPort;
|
||||
port_id gReplyPort;
|
||||
|
||||
|
||||
status_t
|
||||
Serialize(char** _reply, uint32* _totalSize, const struct addrinfo* ai)
|
||||
{
|
||||
uint32 addrsSize = ai == NULL ? 0 : sizeof(addrinfo);
|
||||
uint32 namesSize = 0;
|
||||
uint32 socksSize = 0;
|
||||
|
||||
const struct addrinfo* current = ai;
|
||||
while (current != NULL) {
|
||||
if (current->ai_canonname != NULL)
|
||||
namesSize += strlen(current->ai_canonname) + 1;
|
||||
if (current->ai_addr != NULL) {
|
||||
if (current->ai_family == AF_INET)
|
||||
socksSize += sizeof(sockaddr_in);
|
||||
else
|
||||
socksSize += sizeof(sockaddr_in6);
|
||||
}
|
||||
if (current->ai_next != NULL)
|
||||
addrsSize += sizeof(addrinfo);
|
||||
current = current->ai_next;
|
||||
}
|
||||
|
||||
uint32 totalSize = addrsSize + namesSize + socksSize;
|
||||
char* reply = reinterpret_cast<char*>(malloc(totalSize));
|
||||
if (reply == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint32 addrPos = 0;
|
||||
uint32 namePos = addrsSize;
|
||||
uint32 sockPos = addrsSize + namesSize;
|
||||
|
||||
struct addrinfo temp;
|
||||
|
||||
current = ai;
|
||||
while (current != NULL) {
|
||||
memcpy(&temp, current, sizeof(addrinfo));
|
||||
|
||||
if (current->ai_canonname != NULL) {
|
||||
strcpy(reply + namePos, current->ai_canonname);
|
||||
uint32 nSize = strlen(current->ai_canonname) + 1;
|
||||
temp.ai_canonname = reinterpret_cast<char*>(namePos);
|
||||
namePos += nSize;
|
||||
}
|
||||
if (current->ai_addr != NULL) {
|
||||
if (current->ai_family == AF_INET) {
|
||||
memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in));
|
||||
temp.ai_addr = reinterpret_cast<sockaddr*>(sockPos);
|
||||
sockPos += sizeof(sockaddr_in);
|
||||
} else {
|
||||
memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in6));
|
||||
temp.ai_addr = reinterpret_cast<sockaddr*>(sockPos);
|
||||
sockPos += sizeof(sockaddr_in6);
|
||||
}
|
||||
}
|
||||
|
||||
addrinfo* next = current->ai_next;
|
||||
if (next != NULL)
|
||||
temp.ai_next = reinterpret_cast<addrinfo*>(addrPos) + 1;
|
||||
else
|
||||
temp.ai_next = NULL;
|
||||
|
||||
memcpy(reply + addrPos, &temp, sizeof(addrinfo));
|
||||
addrPos += sizeof(addrinfo);
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
*_reply = reply;
|
||||
*_totalSize = totalSize;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
GetAddrInfo(const char* buffer)
|
||||
{
|
||||
const char* node = buffer[0] == '\0' ? NULL : buffer;
|
||||
uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1;
|
||||
|
||||
const char* service = buffer[nodeSize] == '\0' ? NULL : buffer + nodeSize;
|
||||
uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1;
|
||||
|
||||
const struct addrinfo* hints
|
||||
= reinterpret_cast<const addrinfo*>(buffer + nodeSize + serviceSize);
|
||||
|
||||
struct addrinfo* ai;
|
||||
status_t result = getaddrinfo(node, service, hints, &ai);
|
||||
if (result != B_OK)
|
||||
return write_port(gReplyPort, MsgError, &result, sizeof(result));
|
||||
|
||||
uint32 totalSize;
|
||||
char* reply;
|
||||
result = Serialize(&reply, &totalSize, ai);
|
||||
freeaddrinfo(ai);
|
||||
if (result != B_OK)
|
||||
return write_port(gReplyPort, MsgError, &result, sizeof(result));
|
||||
return write_port(gReplyPort, MsgReply, reply, totalSize);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
MainLoop()
|
||||
{
|
||||
do {
|
||||
ssize_t size = port_buffer_size(gRequestPort);
|
||||
if (size < B_OK)
|
||||
return 0;
|
||||
|
||||
void* buffer = malloc(size);
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
MemoryDeleter _(buffer);
|
||||
|
||||
int32 code;
|
||||
size = read_port(gRequestPort, &code, buffer, size);
|
||||
if (size < B_OK)
|
||||
return 0;
|
||||
|
||||
status_t result;
|
||||
switch (code) {
|
||||
case MsgGetAddrInfo:
|
||||
result = GetAddrInfo(reinterpret_cast<char*>(buffer));
|
||||
default:
|
||||
result = B_BAD_VALUE;
|
||||
write_port(gReplyPort, MsgError, &result, sizeof(result));
|
||||
result = B_OK;
|
||||
}
|
||||
|
||||
if (result != B_OK)
|
||||
return 0;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
gRequestPort = find_port(kPortNameReq);
|
||||
if (gRequestPort < B_OK) {
|
||||
fprintf(stderr, "%s\n", strerror(gRequestPort));
|
||||
return gRequestPort;
|
||||
}
|
||||
|
||||
gReplyPort = find_port(kPortNameRpl);
|
||||
if (gReplyPort < B_OK) {
|
||||
fprintf(stderr, "%s\n", strerror(gReplyPort));
|
||||
return gReplyPort;
|
||||
}
|
||||
|
||||
return MainLoop();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <FindDirectory.h>
|
||||
#include <StorageDefs.h>
|
||||
|
||||
#include <errno_private.h>
|
||||
|
||||
|
||||
static status_t
|
||||
get_path(char *path, bool create)
|
||||
{
|
||||
status_t status = find_directory(B_COMMON_SETTINGS_DIRECTORY, -1, create,
|
||||
path, B_PATH_NAME_LENGTH);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
strlcat(path, "/network", B_PATH_NAME_LENGTH);
|
||||
if (create)
|
||||
mkdir(path, 0755);
|
||||
strlcat(path, "/hostname", B_PATH_NAME_LENGTH);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
extern "C" int
|
||||
sethostname(const char *hostName, size_t nameSize)
|
||||
{
|
||||
char path[B_PATH_NAME_LENGTH];
|
||||
if (get_path(path, false) != B_OK) {
|
||||
__set_errno(B_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int file = open(path, O_WRONLY | O_CREAT, 0644);
|
||||
if (file < 0)
|
||||
return -1;
|
||||
|
||||
nameSize = min_c(nameSize, MAXHOSTNAMELEN);
|
||||
|
||||
if (write(file, hostName, nameSize) != (ssize_t)nameSize
|
||||
|| write(file, "\n", 1) != 1) {
|
||||
close(file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
extern "C" int
|
||||
gethostname(char *hostName, size_t nameSize)
|
||||
{
|
||||
// look up hostname from network settings hostname file
|
||||
|
||||
char path[B_PATH_NAME_LENGTH];
|
||||
if (get_path(path, false) != B_OK) {
|
||||
__set_errno(B_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int file = open(path, O_RDONLY);
|
||||
if (file < 0)
|
||||
return -1;
|
||||
|
||||
nameSize = min_c(nameSize, MAXHOSTNAMELEN);
|
||||
|
||||
int length = read(file, hostName, nameSize - 1);
|
||||
close(file);
|
||||
|
||||
if (length < 0)
|
||||
return -1;
|
||||
|
||||
hostName[length] = '\0';
|
||||
|
||||
char *end = strpbrk(hostName, "\r\n\t");
|
||||
if (end != NULL)
|
||||
end[0] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 1983, 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies, and that
|
||||
* the name of Digital Equipment Corporation not be used in advertising or
|
||||
* publicity pertaining to distribution of the document or software without
|
||||
* specific, written prior permission.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
|
||||
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
|
||||
* Portions Copyright (c) 1996-1999 by Internet Software Consortium.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*%
|
||||
* Ascii internet address interpretation routine.
|
||||
* The value returned is in network order.
|
||||
*/
|
||||
in_addr_t
|
||||
inet_addr(const char *cp) {
|
||||
struct in_addr val;
|
||||
|
||||
if (inet_aton(cp, &val))
|
||||
return (val.s_addr);
|
||||
return (INADDR_NONE);
|
||||
}
|
||||
|
||||
/*%
|
||||
* Check whether "cp" is a valid ascii representation
|
||||
* of an Internet address and convert to a binary address.
|
||||
* Returns 1 if the address is valid, 0 if not.
|
||||
* This replaces inet_addr, the return value from which
|
||||
* cannot distinguish between failure and a local broadcast address.
|
||||
*/
|
||||
int
|
||||
inet_aton(const char *cp, struct in_addr *addr) {
|
||||
u_long val;
|
||||
int base, n;
|
||||
char c;
|
||||
u_int8_t parts[4];
|
||||
u_int8_t *pp = parts;
|
||||
int digit;
|
||||
|
||||
c = *cp;
|
||||
for (;;) {
|
||||
/*
|
||||
* Collect number up to ``.''.
|
||||
* Values are specified as for C:
|
||||
* 0x=hex, 0=octal, isdigit=decimal.
|
||||
*/
|
||||
if (!isdigit((unsigned char)c))
|
||||
return (0);
|
||||
val = 0; base = 10; digit = 0;
|
||||
if (c == '0') {
|
||||
c = *++cp;
|
||||
if (c == 'x' || c == 'X')
|
||||
base = 16, c = *++cp;
|
||||
else {
|
||||
base = 8;
|
||||
digit = 1 ;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (isascii(c) && isdigit((unsigned char)c)) {
|
||||
if (base == 8 && (c == '8' || c == '9'))
|
||||
return (0);
|
||||
val = (val * base) + (c - '0');
|
||||
c = *++cp;
|
||||
digit = 1;
|
||||
} else if (base == 16 && isascii(c) &&
|
||||
isxdigit((unsigned char)c)) {
|
||||
val = (val << 4) |
|
||||
(c + 10 - (islower((unsigned char)c) ? 'a' : 'A'));
|
||||
c = *++cp;
|
||||
digit = 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (c == '.') {
|
||||
/*
|
||||
* Internet format:
|
||||
* a.b.c.d
|
||||
* a.b.c (with c treated as 16 bits)
|
||||
* a.b (with b treated as 24 bits)
|
||||
*/
|
||||
if (pp >= parts + 3 || val > 0xffU)
|
||||
return (0);
|
||||
*pp++ = val;
|
||||
c = *++cp;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Check for trailing characters.
|
||||
*/
|
||||
if (c != '\0' && (!isascii(c) || !isspace((unsigned char)c)))
|
||||
return (0);
|
||||
/*
|
||||
* Did we get a valid digit?
|
||||
*/
|
||||
if (!digit)
|
||||
return (0);
|
||||
/*
|
||||
* Concoct the address according to
|
||||
* the number of parts specified.
|
||||
*/
|
||||
n = pp - parts + 1;
|
||||
switch (n) {
|
||||
case 1: /*%< a -- 32 bits */
|
||||
break;
|
||||
|
||||
case 2: /*%< a.b -- 8.24 bits */
|
||||
if (val > 0xffffffU)
|
||||
return (0);
|
||||
val |= parts[0] << 24;
|
||||
break;
|
||||
|
||||
case 3: /*%< a.b.c -- 8.8.16 bits */
|
||||
if (val > 0xffffU)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16);
|
||||
break;
|
||||
|
||||
case 4: /*%< a.b.c.d -- 8.8.8.8 bits */
|
||||
if (val > 0xffU)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
|
||||
break;
|
||||
}
|
||||
if (addr != NULL)
|
||||
addr->s_addr = htonl(val);
|
||||
return (1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
|
||||
* Copyright (c) 1996-1999 by Internet Software Consortium.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static const char rcsid[] = "$Id: inet_ntop.c,v 1.5 2005/11/03 22:59:52 marka Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/nameser.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef SPRINTF_CHAR
|
||||
# define SPRINTF(x) strlen(sprintf/**/x)
|
||||
#else
|
||||
# define SPRINTF(x) ((size_t)sprintf x)
|
||||
#endif
|
||||
|
||||
/*%
|
||||
* WARNING: Don't even consider trying to compile this on a system where
|
||||
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
|
||||
*/
|
||||
|
||||
static const char *inet_ntop4 __P((const u_char *src, char *dst, size_t size));
|
||||
static const char *inet_ntop6 __P((const u_char *src, char *dst, size_t size));
|
||||
|
||||
/* char *
|
||||
* inet_ntop(af, src, dst, size)
|
||||
* convert a network format address to presentation format.
|
||||
* return:
|
||||
* pointer to presentation format address (`dst'), or NULL (see errno).
|
||||
* author:
|
||||
* Paul Vixie, 1996.
|
||||
*/
|
||||
const char *
|
||||
inet_ntop(af, src, dst, size)
|
||||
int af;
|
||||
const void *src;
|
||||
char *dst;
|
||||
socklen_t size;
|
||||
{
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
return (inet_ntop4(src, dst, size));
|
||||
case AF_INET6:
|
||||
return (inet_ntop6(src, dst, size));
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return (NULL);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/* const char *
|
||||
* inet_ntop4(src, dst, size)
|
||||
* format an IPv4 address
|
||||
* return:
|
||||
* `dst' (as a const)
|
||||
* notes:
|
||||
* (1) uses no statics
|
||||
* (2) takes a u_char* not an in_addr as input
|
||||
* author:
|
||||
* Paul Vixie, 1996.
|
||||
*/
|
||||
static const char *
|
||||
inet_ntop4(src, dst, size)
|
||||
const u_char *src;
|
||||
char *dst;
|
||||
size_t size;
|
||||
{
|
||||
static const char fmt[] = "%u.%u.%u.%u";
|
||||
char tmp[sizeof "255.255.255.255"];
|
||||
|
||||
if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) >= size) {
|
||||
errno = ENOSPC;
|
||||
return (NULL);
|
||||
}
|
||||
strcpy(dst, tmp);
|
||||
return (dst);
|
||||
}
|
||||
|
||||
/* const char *
|
||||
* inet_ntop6(src, dst, size)
|
||||
* convert IPv6 binary address into presentation (printable) format
|
||||
* author:
|
||||
* Paul Vixie, 1996.
|
||||
*/
|
||||
static const char *
|
||||
inet_ntop6(src, dst, size)
|
||||
const u_char *src;
|
||||
char *dst;
|
||||
size_t size;
|
||||
{
|
||||
/*
|
||||
* Note that int32_t and int16_t need only be "at least" large enough
|
||||
* to contain a value of the specified size. On some systems, like
|
||||
* Crays, there is no such thing as an integer variable with 16 bits.
|
||||
* Keep this in mind if you think this function should have been coded
|
||||
* to use pointer overlays. All the world's not a VAX.
|
||||
*/
|
||||
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
|
||||
struct { int base, len; } best, cur;
|
||||
u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Preprocess:
|
||||
* Copy the input (bytewise) array into a wordwise array.
|
||||
* Find the longest run of 0x00's in src[] for :: shorthanding.
|
||||
*/
|
||||
memset(words, '\0', sizeof words);
|
||||
for (i = 0; i < NS_IN6ADDRSZ; i++)
|
||||
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
|
||||
best.base = -1;
|
||||
best.len = 0;
|
||||
cur.base = -1;
|
||||
cur.len = 0;
|
||||
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
|
||||
if (words[i] == 0) {
|
||||
if (cur.base == -1)
|
||||
cur.base = i, cur.len = 1;
|
||||
else
|
||||
cur.len++;
|
||||
} else {
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
cur.base = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
}
|
||||
if (best.base != -1 && best.len < 2)
|
||||
best.base = -1;
|
||||
|
||||
/*
|
||||
* Format the result.
|
||||
*/
|
||||
tp = tmp;
|
||||
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
|
||||
/* Are we inside the best run of 0x00's? */
|
||||
if (best.base != -1 && i >= best.base &&
|
||||
i < (best.base + best.len)) {
|
||||
if (i == best.base)
|
||||
*tp++ = ':';
|
||||
continue;
|
||||
}
|
||||
/* Are we following an initial run of 0x00s or any real hex? */
|
||||
if (i != 0)
|
||||
*tp++ = ':';
|
||||
/* Is this address an encapsulated IPv4? */
|
||||
if (i == 6 && best.base == 0 && (best.len == 6 ||
|
||||
(best.len == 7 && words[7] != 0x0001) ||
|
||||
(best.len == 5 && words[5] == 0xffff))) {
|
||||
if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
|
||||
return (NULL);
|
||||
tp += strlen(tp);
|
||||
break;
|
||||
}
|
||||
tp += SPRINTF((tp, "%x", words[i]));
|
||||
}
|
||||
/* Was it a trailing run of 0x00's? */
|
||||
if (best.base != -1 && (best.base + best.len) ==
|
||||
(NS_IN6ADDRSZ / NS_INT16SZ))
|
||||
*tp++ = ':';
|
||||
*tp++ = '\0';
|
||||
|
||||
/*
|
||||
* Check for overflow, copy, and we're done.
|
||||
*/
|
||||
if ((size_t)(tp - tmp) > size) {
|
||||
errno = ENOSPC;
|
||||
return (NULL);
|
||||
}
|
||||
strcpy(dst, tmp);
|
||||
return (dst);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user