diff --git a/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp b/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp index dd7f0f76ca..3a561f05b5 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp @@ -11,14 +11,21 @@ #include -#include "Request.h" +#include +#include +#include "Request.h" #include "Inode.h" +extern RPC::ServerManager* gRPCServerManager; +extern RPC::ProgramData* CreateNFS4Server(RPC::Server* serv); + + Filesystem::Filesystem() : fPath(NULL), + fName(NULL), fId(1) { } @@ -26,10 +33,27 @@ Filesystem::Filesystem() Filesystem::~Filesystem() { + free(const_cast(fName)); free(const_cast(fPath)); } +static const char* +sGetPath(const char* root, const char* path) +{ + int slash = 0; + for (int i = 0; path[i] != '\0'; i++) { + if (path[i] != root[i] || root[i] == '\0') + break; + + if (path[i] == '/') + slash = i; + } + + return path + slash; +} + + status_t Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath, dev_t id) @@ -67,7 +91,7 @@ Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath, req.GetFH(); req.Access(); - Attribute attr[] = { FATTR4_FSID }; + Attribute attr[] = { FATTR4_FSID, FATTR4_FS_LOCATIONS }; req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); status_t result = request.Send(); @@ -110,15 +134,39 @@ Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath, reinterpret_cast(values[0].fData.fPointer); Filesystem* fs = new(std::nothrow) Filesystem; + + if (count == 2 && values[1].fAttribute == FATTR4_FS_LOCATIONS) { + FSLocations* locs = + reinterpret_cast(values[1].fData.fLocations); + + fs->fPath = strdup(locs->fRootPath); + + delete locs; + } else + fs->fPath = NULL; + + const char* name; if (fsPath != NULL && fsPath[0] == '/') - fs->fPath = strdup(fsPath + 1); - else - fs->fPath = strdup(fsPath + 1); + fsPath++; + name = strrchr(fsPath, '/'); + if (name != NULL) { + name++; + fs->fName = strdup(name); + } else + fs->fName = strdup(fsPath); + memcpy(&fs->fRootFH, &fh, sizeof(Filehandle)); fs->fServer = serv; fs->fDevId = id; fs->fFsId = *fsid; + FileInfo fi; + fi.fFH = fh; + fi.fParent = fh; + fi.fName = strdup("/"); + fi.fPath = strdup(sGetPath(fs->fPath, fsPath)); + fs->fRoot = fi; + *pfs = fs; return B_OK; @@ -149,14 +197,8 @@ Filesystem::GetInode(ino_t id, Inode** _inode) Inode* Filesystem::CreateRootInode() { - FileInfo fi; - fi.fFH = fRootFH; - fi.fParent = fRootFH; - fi.fName = strdup("/"); - fi.fPath = strdup(fPath); - Inode* inode; - status_t result = Inode::CreateInode(this, fi, &inode); + status_t result = Inode::CreateInode(this, fRoot, &inode); if (result == B_OK) return inode; else @@ -229,12 +271,95 @@ Filesystem::ReadInfo(struct fs_info* info) } info->flags = B_FS_IS_READONLY; - const char* name = strrchr(fPath, '/'); - if (name != NULL) { - name++; - strncpy(info->volume_name, name, B_FILE_NAME_LENGTH); - } else - strncpy(info->volume_name, fPath, B_FILE_NAME_LENGTH); + strncpy(info->volume_name, fName, B_FILE_NAME_LENGTH); + + return B_OK; +} + + +status_t +Filesystem::Migrate(const Filehandle& fh, const RPC::Server* serv) +{ + mutex_lock(&fMigrationLock); + if (serv != fServer) { + mutex_unlock(&fMigrationLock); + return B_OK; + } + + Request request(fServer); + RequestBuilder& req = request.Builder(); + + req.PutFH(fh); + Attribute attr[] = { FATTR4_FS_LOCATIONS }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) { + mutex_unlock(&fMigrationLock); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + result = reply.PutFH(); + if (result != B_OK) { + mutex_unlock(&fMigrationLock); + return result; + } + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK || count < 1) { + mutex_unlock(&fMigrationLock); + return result; + } + + FSLocations* locs = + reinterpret_cast(values[0].fData.fLocations); + + dns_resolver_module* dns; + result = get_module(DNS_RESOLVER_MODULE_NAME, + reinterpret_cast(&dns)); + if (result != B_OK) { + mutex_unlock(&fMigrationLock); + return result; + } + + RPC::Server* server = fServer; + for (uint32 i = 0; i < locs->fCount; i++) { + for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) { + uint32 ip; + struct in_addr addr; + if (inet_aton(locs->fLocations[i].fLocations[j], &addr) == 0) { + result = dns->dns_resolve(locs->fLocations[i].fLocations[j], + &ip); + if (result != B_OK) + continue; + } else + ip = addr.s_addr; + + if (gRPCServerManager->Acquire(&fServer, ip, 2049, + ProtocolUDP, CreateNFS4Server) == B_OK) { + + free(const_cast(fPath)); + fPath = strdup(locs->fLocations[j].fRootPath); + break; + } + } + } + + put_module(DNS_RESOLVER_MODULE_NAME); + delete locs; + + if (server == fServer) { + mutex_unlock(&fMigrationLock); + return B_ERROR; + } + + gRPCServerManager->Release(server); + + mutex_unlock(&fMigrationLock); return B_OK; } diff --git a/src/add-ons/kernel/file_systems/nfs4/Filesystem.h b/src/add-ons/kernel/file_systems/nfs4/Filesystem.h index 3ddc2ef575..ed11fc2f89 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Filesystem.h +++ b/src/add-ons/kernel/file_systems/nfs4/Filesystem.h @@ -30,9 +30,13 @@ public: status_t ReadInfo(struct fs_info* info); + status_t Migrate(const Filehandle& fh, + const RPC::Server* serv); + inline RPC::Server* Server(); inline NFS4Server* NFSServer(); + inline const char* Path() const; inline const FilesystemId& FsId() const; inline uint64 AllocFileId(); @@ -42,9 +46,12 @@ public: private: Filesystem(); - const char* fPath; FilesystemId fFsId; + const char* fPath; + mutex fMigrationLock; + const char* fName; + FileInfo fRoot; Filehandle fRootFH; RPC::Server* fServer; @@ -70,6 +77,13 @@ Filesystem::NFSServer() } +inline const char* +Filesystem::Path() const +{ + return fPath; +} + + inline const FilesystemId& Filesystem::FsId() const { diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp index d49d143da3..9ce8efcc94 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp @@ -37,7 +37,8 @@ Inode::CreateInode(Filesystem* fs, const FileInfo &fi, Inode** _inode) inode->fPath = strdup(fi.fPath); do { - Request request(fs->Server()); + RPC::Server* serv = fs->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(inode->fHandle); @@ -57,6 +58,12 @@ Inode::CreateInode(Filesystem* fs, const FileInfo &fi, Inode** _inode) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fs->Migrate(inode->fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -113,7 +120,8 @@ Inode::LookUp(const char* name, ino_t* id) } do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -140,6 +148,12 @@ Inode::LookUp(const char* name, ino_t* id) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -206,7 +220,8 @@ Inode::ReadLink(void* buffer, size_t* length) return B_BAD_VALUE; do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -224,6 +239,12 @@ Inode::ReadLink(void* buffer, size_t* length) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -241,7 +262,8 @@ status_t Inode::Access(int mode) { do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -259,6 +281,12 @@ Inode::Access(int mode) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -293,7 +321,8 @@ status_t Inode::Stat(struct stat* st) { do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -315,6 +344,12 @@ Inode::Stat(struct stat* st) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -419,7 +454,8 @@ Inode::Open(int mode, OpenFileCookie* cookie) do { cookie->fClientId = fFilesystem->NFSServer()->ClientId(); - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); cookie->fOwnerId = atomic_add64(&cookie->fLastOwnerId, 1); @@ -440,6 +476,12 @@ Inode::Open(int mode, OpenFileCookie* cookie) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + // server is in grace period, we need to wait if (reply.NFS4Error() == NFS4ERR_GRACE) { fFilesystem->NFSServer()->ReleaseCID(cookie->fClientId); @@ -501,7 +543,8 @@ Inode::Close(OpenFileCookie* cookie) fFilesystem->NFSServer()->RemoveOpenFile(cookie); do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -520,6 +563,12 @@ Inode::Close(OpenFileCookie* cookie) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + // server is in grace period, we need to wait if (reply.NFS4Error() == NFS4ERR_GRACE) { snooze_etc(fFilesystem->NFSServer()->LeaseTime() / 3, @@ -553,7 +602,8 @@ Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length) while (size < *_length && !eof) { do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -572,6 +622,12 @@ Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + // server is in grace period, we need to wait if (reply.NFS4Error() == NFS4ERR_GRACE) { snooze_etc(fFilesystem->NFSServer()->LeaseTime() / 3, @@ -614,7 +670,8 @@ Inode::OpenDir(uint64* cookie) return B_NOT_A_DIRECTORY; do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -632,6 +689,12 @@ Inode::OpenDir(uint64* cookie) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -657,7 +720,8 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, uint64* cookie, bool* eof) { do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -677,6 +741,12 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, uint64* cookie, continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -712,7 +782,8 @@ status_t Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size) { do { - Request request(fFilesystem->Server()); + RPC::Server* serv = fFilesystem->Server(); + Request request(serv); RequestBuilder& req = request.Builder(); req.PutFH(fHandle); @@ -733,6 +804,12 @@ Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size) continue; } + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + fFilesystem->Migrate(fHandle, serv); + continue; + } + result = reply.PutFH(); if (result != B_OK) return result; @@ -849,16 +926,10 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, uint64* cookie) } -status_t -Inode::_LookUpFilehandle() +static status_t +sParsePath(RequestBuilder& req, uint32* count, const char* _path) { - Request request(fFilesystem->Server()); - RequestBuilder& req = request.Builder(); - - req.PutRootFH(); - - uint32 lookupCount = 0; - char* path = strdup(fPath); + char* path = strdup(_path); char* pathStart = path; char* pathEnd; while (pathStart != NULL) { @@ -873,10 +944,27 @@ Inode::_LookUpFilehandle() else pathStart = NULL; - lookupCount++; + (*count)++; } free(path); + return B_OK; +} + + +status_t +Inode::_LookUpFilehandle() +{ + Request request(fFilesystem->Server()); + RequestBuilder& req = request.Builder(); + + req.PutRootFH(); + + uint32 lookupCount = 0; + + sParsePath(req, &lookupCount, fFilesystem->Path()); + sParsePath(req, &lookupCount, fPath); + req.GetFH(); status_t result = request.Send(); diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp index 092b2ff01d..01129a5842 100644 --- a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp @@ -14,6 +14,22 @@ #include +FSLocation::~FSLocation() +{ + free(const_cast(fRootPath)); + for (uint32 i = 0; i < fCount; i++) + free(const_cast(fLocations[i])); + delete[] fLocations; +} + + +FSLocations::~FSLocations() +{ + free(const_cast(fRootPath)); + delete[] fLocations; +} + + AttrValue::AttrValue() : fFreePointer(false) @@ -25,6 +41,8 @@ AttrValue::~AttrValue() { if (fFreePointer) free(fData.fPointer); + if (fAttribute == FATTR4_FS_LOCATIONS) + delete fData.fLocations; } @@ -285,6 +303,30 @@ ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier) } +static const char* +sFlattenPathname(XDR::ReadStream& str) +{ + uint32 count = str.GetUInt(); + char* pathname = NULL; + uint32 size = 0; + for (uint32 i = 0; i < count; i++) { + const char* path = str.GetString(); + size += strlen(path) + 1; + if (pathname == NULL) { + pathname = reinterpret_cast(malloc(strlen(path + 1))); + pathname[0] = '\0'; + } else { + *pathname++ = '/'; + pathname = reinterpret_cast(realloc(pathname, size)); + } + strcat(pathname, path); + free(const_cast(path)); + } + + return pathname; +} + + status_t ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs, uint32* count) @@ -374,6 +416,25 @@ ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs, 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(); diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h index b3f0901ece..f49c2252a7 100644 --- a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h @@ -15,6 +15,22 @@ #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(); @@ -22,9 +38,10 @@ struct AttrValue { uint8 fAttribute; bool fFreePointer; union { - uint32 fValue32; - uint64 fValue64; - void* fPointer; + uint32 fValue32; + uint64 fValue64; + void* fPointer; + FSLocations* fLocations; } fData; }; 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 0d7e81ddbe..4f48d66037 100644 --- a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp @@ -32,8 +32,8 @@ dprintf(const char* format, ...); RPC::ServerManager* gRPCServerManager; -static RPC::ProgramData* -sCreateNFS4Server(RPC::Server* serv) +RPC::ProgramData* +CreateNFS4Server(RPC::Server* serv) { return new NFS4Server(serv); } @@ -96,7 +96,7 @@ nfs4_mount(fs_volume* volume, const char* device, uint32 flags, RPC::Server *server; result = gRPCServerManager->Acquire(&server, ip, 2049, ProtocolUDP, - sCreateNFS4Server); + CreateNFS4Server); if (result != B_OK) return result;