From a0d5a922d6440c2d4df51536fab9ef5057e021f5 Mon Sep 17 00:00:00 2001 From: Pawel Dziepak Date: Tue, 5 Feb 2013 03:20:37 +0100 Subject: [PATCH] nfs4: Try connecting to all getaddrinfo() results before giving up --- .../kernel/file_systems/nfs4/Connection.cpp | 98 +++++++++++++++---- .../kernel/file_systems/nfs4/Connection.h | 28 +++++- .../kernel/file_systems/nfs4/FileSystem.cpp | 7 +- .../kernel/file_systems/nfs4/RPCServer.cpp | 19 +++- .../kernel/file_systems/nfs4/RPCServer.h | 4 +- .../file_systems/nfs4/kernel_interface.cpp | 23 +++-- 6 files changed, 140 insertions(+), 39 deletions(-) diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.cpp b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp index 371cc395a6..e9f86785da 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Connection.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp @@ -189,17 +189,36 @@ PeerAddress::InAddrSize() const } +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 -PeerAddress::ResolveName(const char* name, PeerAddress* address) +AddressResolver::ResolveAddress(const char* name) { ASSERT(name != NULL); - ASSERT(address != NULL); - address->fProtocol = IPPROTO_TCP; + 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) { @@ -207,35 +226,74 @@ PeerAddress::ResolveName(const char* name, PeerAddress* address) addr.sin_family = AF_INET; addr.sin_port = htons(NFS4_PORT); - memcpy(&address->fAddress, &addr, sizeof(addr)); + memcpy(&fAddress.fAddress, &addr, sizeof(addr)); + fAddress.fProtocol = IPPROTO_TCP; return B_OK; } - addrinfo* ai; - status_t result = getaddrinfo(name, NULL, NULL, &ai); - if (result != B_OK) - return result; + status_t result = getaddrinfo(name, NULL, NULL, &fHead); + fCurrent = fHead; - addrinfo* current = ai; - while (current != NULL) { - if (current->ai_family == AF_INET) { - memcpy(&address->fAddress, current->ai_addr, sizeof(sockaddr_in)); + return result; +} + + +void +AddressResolver::ForceProtocol(const char* protocol) +{ + ASSERT(protocol != NULL); + + if (strcmp(protocol, "tcp") == 0) + fForcedProtocol = IPPROTO_TCP; + else if (strcmp(protocol, "udp") == 0) + fForcedProtocol = IPPROTO_UDP; + + fAddress.SetProtocol(protocol); +} + + +void +AddressResolver::ForcePort(uint16 port) +{ + fForcedPort = htons(port); + fAddress.SetPort(port); +} + + +status_t +AddressResolver::GetNextAddress(PeerAddress* address) +{ + ASSERT(address != NULL); + + if (fStatus != B_OK) + return fStatus; + + if (fHead == NULL) { + *address = fAddress; + fStatus = B_NAME_NOT_FOUND; + return B_OK; + } + + address->fProtocol = fForcedProtocol; + + while (fCurrent != NULL) { + if (fCurrent->ai_family == AF_INET) { + memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in)); reinterpret_cast(&address->fAddress)->sin_port - = htons(NFS4_PORT); - } else if (current->ai_family == AF_INET6) { - memcpy(&address->fAddress, current->ai_addr, sizeof(sockaddr_in6)); + = fForcedPort; + } else if (fCurrent->ai_family == AF_INET6) { + memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in6)); reinterpret_cast(&address->fAddress)->sin6_port - = htons(NFS4_PORT); + = fForcedPort; } else { - current = current->ai_next; + fCurrent = fCurrent->ai_next; continue; } - freeaddrinfo(ai); + fCurrent = fCurrent->ai_next; return B_OK; } - freeaddrinfo(ai); return B_NAME_NOT_FOUND; } diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.h b/src/add-ons/kernel/file_systems/nfs4/Connection.h index aceb2c0742..16f8062058 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Connection.h +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.h @@ -38,9 +38,33 @@ struct PeerAddress { const void* InAddr() const; size_t InAddrSize() const; +}; - static status_t ResolveName(const char* name, - PeerAddress* address); +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 { diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp index e76a010dea..a877cb9ded 100644 --- a/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp @@ -257,14 +257,11 @@ FileSystem::Migrate(const RPC::Server* serv) = reinterpret_cast(values[0].fData.fLocations); RPC::Server* server = fServer; - PeerAddress addr = fServer->ID(); for (uint32 i = 0; i < locs->fCount; i++) { for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) { - if (PeerAddress::ResolveName(locs->fLocations[i].fLocations[j], - &addr) != B_OK) - continue; + AddressResolver resolver(locs->fLocations[i].fLocations[j]); - if (gRPCServerManager->Acquire(&fServer, addr, + if (gRPCServerManager->Acquire(&fServer, &resolver, CreateNFS4Server) == B_OK) { free(const_cast(fPath)); diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp index f80633e906..13f6a5fbb6 100644 --- a/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp @@ -312,7 +312,24 @@ ServerManager::~ServerManager() status_t -ServerManager::Acquire(Server** _server, const PeerAddress& address, +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); diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCServer.h b/src/add-ons/kernel/file_systems/nfs4/RPCServer.h index 738162d92f..9421049918 100644 --- a/src/add-ons/kernel/file_systems/nfs4/RPCServer.h +++ b/src/add-ons/kernel/file_systems/nfs4/RPCServer.h @@ -171,11 +171,13 @@ public: ServerManager(); ~ServerManager(); - status_t Acquire(Server** _server, const PeerAddress& address, + 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); diff --git a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp index 7abba1c746..630e1e9f9a 100644 --- a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp @@ -72,7 +72,7 @@ CreateNFS4Server(RPC::Server* serv) // port=X - connect to port X (default: 2049) // proto=X - user transport protocol X (default: tcp) static status_t -ParseArguments(const char* _args, PeerAddress* address, char** _path, +ParseArguments(const char* _args, AddressResolver** address, char** _path, MountConfiguration* conf) { if (_args == NULL) @@ -92,13 +92,15 @@ ParseArguments(const char* _args, PeerAddress* address, char** _path, return B_MISMATCHED_VALUES; *path++ = '\0'; - status_t result = PeerAddress::ResolveName(args, address); - if (result != B_OK) - return result; + *address = new AddressResolver(args); + if (*address == NULL) + return B_NO_MEMORY; *_path = strdup(path); - if (*_path == NULL) + if (*_path == NULL) { + delete *address; return B_NO_MEMORY; + } conf->fHard = false; conf->fRetryLimit = 5; @@ -127,10 +129,10 @@ ParseArguments(const char* _args, PeerAddress* address, char** _path, conf->fEmulateNamedAttrs = true; else if (strncmp(options, "port=", 5) == 0) { options += strlen("port="); - address->SetPort(atoi(options)); + (*address)->ForcePort(atoi(options)); } else if (strncmp(options, "proto=", 6) == 0) { options += strlen("proto="); - address->SetProtocol(options); + (*address)->ForceProtocol(options); } options = optionsEnd; @@ -165,16 +167,17 @@ nfs4_mount(fs_volume* volume, const char* device, uint32 flags, } locker.Unlock(); - PeerAddress address; + AddressResolver* resolver; MountConfiguration config; char* path; - result = ParseArguments(args, &address, &path, &config); + result = ParseArguments(args, &resolver, &path, &config); if (result != B_OK) return result; MemoryDeleter pathDeleter(path); RPC::Server* server; - result = gRPCServerManager->Acquire(&server, address, CreateNFS4Server); + result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server); + delete resolver; if (result != B_OK) return result;