nfs4: Add full support for lease migration
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
#include <net/dns_resolver.h>
|
||||
|
||||
#include "Request.h"
|
||||
#include "Inode.h"
|
||||
#include "RootInode.h"
|
||||
|
||||
|
||||
extern RPC::ServerManager* gRPCServerManager;
|
||||
@@ -30,7 +30,7 @@ Filesystem::Filesystem()
|
||||
fOpenFiles(NULL),
|
||||
fOpenCount(0),
|
||||
fPath(NULL),
|
||||
fName(NULL),
|
||||
fRoot(NULL),
|
||||
fId(1)
|
||||
{
|
||||
mutex_init(&fOpenLock, NULL);
|
||||
@@ -43,8 +43,8 @@ Filesystem::~Filesystem()
|
||||
|
||||
mutex_destroy(&fOpenLock);
|
||||
|
||||
free(const_cast<char*>(fName));
|
||||
free(const_cast<char*>(fPath));
|
||||
delete fRoot;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,33 +155,39 @@ Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath,
|
||||
} else
|
||||
fs->fPath = NULL;
|
||||
|
||||
FileInfo fi;
|
||||
const char* name;
|
||||
if (fsPath != NULL && fsPath[0] == '/')
|
||||
fsPath++;
|
||||
name = strrchr(fsPath, '/');
|
||||
if (name != NULL) {
|
||||
name++;
|
||||
fs->fName = strdup(name);
|
||||
fi.fName = strdup(name);
|
||||
} else
|
||||
fs->fName = strdup(fsPath);
|
||||
fi.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;
|
||||
|
||||
delete[] values;
|
||||
|
||||
Inode* inode;
|
||||
result = Inode::CreateInode(fs, fi, &inode);
|
||||
if (result != B_OK) {
|
||||
delete fs;
|
||||
return result;
|
||||
}
|
||||
|
||||
fs->fRoot = reinterpret_cast<RootInode*>(inode);
|
||||
|
||||
fs->NFSServer()->AddFilesystem(fs);
|
||||
*pfs = fs;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -207,115 +213,19 @@ Filesystem::GetInode(ino_t id, Inode** _inode)
|
||||
}
|
||||
|
||||
|
||||
Inode*
|
||||
Filesystem::CreateRootInode()
|
||||
{
|
||||
Inode* inode;
|
||||
status_t result = Inode::CreateInode(this, fRoot, &inode);
|
||||
if (result == B_OK)
|
||||
return inode;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Filesystem::ReadInfo(struct fs_info* info)
|
||||
Filesystem::Migrate(const RPC::Server* serv)
|
||||
{
|
||||
Request request(fServer);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(fRootFH);
|
||||
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();
|
||||
|
||||
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) {
|
||||
info->free_nodes = values[next].fData.fValue64;
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_FILES_TOTAL) {
|
||||
info->total_nodes = values[next].fData.fValue64;
|
||||
next++;
|
||||
}
|
||||
|
||||
uint64 io_size = LONGLONG_MAX;
|
||||
if (count >= next && values[next].fAttribute == FATTR4_MAXREAD) {
|
||||
io_size = min_c(io_size, values[next].fData.fValue64);
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_MAXWRITE) {
|
||||
io_size = min_c(io_size, values[next].fData.fValue64);
|
||||
next++;
|
||||
}
|
||||
|
||||
if (io_size == LONGLONG_MAX)
|
||||
io_size = 32768;
|
||||
info->io_size = io_size;
|
||||
info->block_size = io_size;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_SPACE_FREE) {
|
||||
info->free_blocks = values[next].fData.fValue64 / io_size;
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_SPACE_TOTAL) {
|
||||
info->total_blocks = values[next].fData.fValue64 / io_size;
|
||||
next++;
|
||||
}
|
||||
|
||||
info->flags = B_FS_IS_READONLY;
|
||||
strncpy(info->volume_name, fName, B_FILE_NAME_LENGTH);
|
||||
|
||||
delete[] values;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Filesystem::Migrate(const Filehandle& fh, const RPC::Server* serv)
|
||||
{
|
||||
MutexLocker _(fMigrationLock);
|
||||
MutexLocker _(fOpenLock);
|
||||
if (serv != fServer)
|
||||
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)
|
||||
return result;
|
||||
|
||||
ReplyInterpreter& reply = request.Reply();
|
||||
|
||||
reply.PutFH();
|
||||
if (!fRoot->ProbeMigration())
|
||||
return B_OK;
|
||||
|
||||
AttrValue* values;
|
||||
uint32 count;
|
||||
result = reply.GetAttr(&values, &count);
|
||||
if (result != B_OK || count < 1)
|
||||
status_t result = fRoot->GetLocations(&values);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
FSLocations* locs =
|
||||
@@ -358,6 +268,10 @@ Filesystem::Migrate(const Filehandle& fh, const RPC::Server* serv)
|
||||
if (server == fServer)
|
||||
return B_ERROR;
|
||||
|
||||
NFS4Server* old = reinterpret_cast<NFS4Server*>(server->PrivateData());
|
||||
old->RemoveFilesystem(this);
|
||||
NFSServer()->AddFilesystem(this);
|
||||
|
||||
gRPCServerManager->Release(server);
|
||||
|
||||
return B_OK;
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
#define FILESYSTEM_H
|
||||
|
||||
|
||||
#include <fs_info.h>
|
||||
|
||||
#include "InodeIdMap.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "NFS4Server.h"
|
||||
|
||||
|
||||
class Inode;
|
||||
class InodeIdMap;
|
||||
class RootInode;
|
||||
|
||||
class Filesystem {
|
||||
public:
|
||||
@@ -26,12 +24,9 @@ public:
|
||||
~Filesystem();
|
||||
|
||||
status_t GetInode(ino_t id, Inode** inode);
|
||||
Inode* CreateRootInode();
|
||||
inline Inode* Root();
|
||||
|
||||
status_t ReadInfo(struct fs_info* info);
|
||||
|
||||
status_t Migrate(const Filehandle& fh,
|
||||
const RPC::Server* serv);
|
||||
status_t Migrate(const RPC::Server* serv);
|
||||
|
||||
OpenFileCookie* OpenFilesLock();
|
||||
void OpenFilesUnlock();
|
||||
@@ -67,11 +62,8 @@ private:
|
||||
|
||||
FilesystemId fFsId;
|
||||
const char* fPath;
|
||||
mutex fMigrationLock;
|
||||
|
||||
const char* fName;
|
||||
FileInfo fRoot;
|
||||
Filehandle fRootFH;
|
||||
RootInode* fRoot;
|
||||
|
||||
RPC::Server* fServer;
|
||||
|
||||
@@ -82,6 +74,13 @@ private:
|
||||
};
|
||||
|
||||
|
||||
inline Inode*
|
||||
Filesystem::Root()
|
||||
{
|
||||
return reinterpret_cast<Inode*>(fRoot);
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
Filesystem::OpenFilesCount()
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <NodeMonitor.h>
|
||||
|
||||
#include "Request.h"
|
||||
#include "RootInode.h"
|
||||
|
||||
|
||||
Inode::Inode()
|
||||
@@ -25,7 +26,12 @@ Inode::Inode()
|
||||
status_t
|
||||
Inode::CreateInode(Filesystem* fs, const FileInfo &fi, Inode** _inode)
|
||||
{
|
||||
Inode* inode = new(std::nothrow) Inode;
|
||||
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;
|
||||
|
||||
@@ -215,8 +221,7 @@ Inode::Link(Inode* dir, const char* name)
|
||||
|
||||
// filesystem has been moved
|
||||
if (reply.NFS4Error() == NFS4ERR_MOVED) {
|
||||
fFilesystem->Migrate(fHandle, serv);
|
||||
dir->fFilesystem->Migrate(dir->fHandle, serv);
|
||||
fFilesystem->Migrate(serv);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -321,8 +326,7 @@ Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName)
|
||||
|
||||
// filesystem has been moved
|
||||
if (reply.NFS4Error() == NFS4ERR_MOVED) {
|
||||
from->fFilesystem->Migrate(from->fHandle, serv);
|
||||
to->fFilesystem->Migrate(to->fHandle, serv);
|
||||
from->fFilesystem->Migrate(serv);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1557,13 +1561,9 @@ Inode::_HandleErrors(uint32 nfs4Error, RPC::Server* serv,
|
||||
return false;
|
||||
|
||||
// filesystem has been moved
|
||||
case NFS4ERR_MOVED:
|
||||
fFilesystem->Migrate(fHandle, serv);
|
||||
return true;
|
||||
|
||||
// lease has been moved, provoke server to return NFS4ERR_MOVED
|
||||
case NFS4ERR_LEASE_MOVED:
|
||||
Access(0);
|
||||
case NFS4ERR_MOVED:
|
||||
fFilesystem->Migrate(serv);
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
status_t ReleaseAllLocks(OpenFileCookie* cookie);
|
||||
|
||||
|
||||
private:
|
||||
protected:
|
||||
Inode();
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ KernelAddon nfs4 :
|
||||
ReplyInterpreter.cpp
|
||||
Request.cpp
|
||||
RequestBuilder.cpp
|
||||
RootInode.cpp
|
||||
RPCAuth.cpp
|
||||
RPCCall.cpp
|
||||
RPCReply.cpp
|
||||
|
||||
@@ -213,6 +213,21 @@ NFS4Server::ClientId(uint64 prevId, bool forceNew)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::FilesystemMigrated()
|
||||
{
|
||||
// reclaim all opened files and held locks from all filesystems
|
||||
MutexLocker _(fFSLock);
|
||||
Filesystem* fs = fFilesystems;
|
||||
while (fs != NULL) {
|
||||
fs->Migrate(fServer);
|
||||
fs = fs->fNext;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
NFS4Server::_GetLeaseTime()
|
||||
{
|
||||
@@ -297,9 +312,14 @@ NFS4Server::_Renewal()
|
||||
request.Builder().Renew(fClientId);
|
||||
request.Send();
|
||||
|
||||
if (request.Reply().NFS4Error() == NFS4ERR_STALE_CLIENTID)
|
||||
ServerRebooted(clientId);
|
||||
// TODO: support NFS4ERR_LEASE_MOVED
|
||||
switch (request.Reply().NFS4Error()) {
|
||||
case NFS4ERR_STALE_CLIENTID:
|
||||
ServerRebooted(clientId);
|
||||
break;
|
||||
case NFS4ERR_LEASE_MOVED:
|
||||
FilesystemMigrated();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
virtual ~NFS4Server();
|
||||
|
||||
uint64 ServerRebooted(uint64 clientId);
|
||||
status_t FilesystemMigrated();
|
||||
|
||||
void AddFilesystem(Filesystem* fs);
|
||||
void RemoveFilesystem(Filesystem* fs);
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 "Request.h"
|
||||
|
||||
|
||||
status_t
|
||||
RootInode::ReadInfo(struct fs_info* info)
|
||||
{
|
||||
do {
|
||||
RPC::Server* serv = fFilesystem->Server();
|
||||
Request request(serv);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(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(), serv))
|
||||
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) {
|
||||
info->free_nodes = values[next].fData.fValue64;
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_FILES_TOTAL) {
|
||||
info->total_nodes = values[next].fData.fValue64;
|
||||
next++;
|
||||
}
|
||||
|
||||
uint64 io_size = LONGLONG_MAX;
|
||||
if (count >= next && values[next].fAttribute == FATTR4_MAXREAD) {
|
||||
io_size = min_c(io_size, values[next].fData.fValue64);
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_MAXWRITE) {
|
||||
io_size = min_c(io_size, values[next].fData.fValue64);
|
||||
next++;
|
||||
}
|
||||
|
||||
if (io_size == LONGLONG_MAX)
|
||||
io_size = 32768;
|
||||
info->io_size = io_size;
|
||||
info->block_size = io_size;
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_SPACE_FREE) {
|
||||
info->free_blocks = values[next].fData.fValue64 / io_size;
|
||||
next++;
|
||||
}
|
||||
|
||||
if (count >= next && values[next].fAttribute == FATTR4_SPACE_TOTAL) {
|
||||
info->total_blocks = values[next].fData.fValue64 / io_size;
|
||||
next++;
|
||||
}
|
||||
|
||||
delete[] values;
|
||||
|
||||
break;
|
||||
} while (true);
|
||||
|
||||
info->flags = 0;
|
||||
strncpy(info->volume_name, fName, B_FILE_NAME_LENGTH);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
RootInode::ProbeMigration()
|
||||
{
|
||||
do {
|
||||
RPC::Server* serv = fFilesystem->Server();
|
||||
Request request(serv);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(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(), serv))
|
||||
continue;
|
||||
|
||||
return false;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
status_t
|
||||
RootInode::GetLocations(AttrValue** attrv)
|
||||
{
|
||||
do {
|
||||
RPC::Server* serv = fFilesystem->Server();
|
||||
Request request(serv);
|
||||
RequestBuilder& req = request.Builder();
|
||||
|
||||
req.PutFH(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(), serv))
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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:
|
||||
status_t ReadInfo(struct fs_info* info);
|
||||
|
||||
bool ProbeMigration();
|
||||
status_t GetLocations(AttrValue** attr);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // ROOTINODE_H
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
#include <fs_interface.h>
|
||||
|
||||
#include "Connection.h"
|
||||
#include "RPCServer.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "Filesystem.h"
|
||||
#include "Inode.h"
|
||||
#include "NFS4Defs.h"
|
||||
#include "RequestBuilder.h"
|
||||
#include "ReplyInterpreter.h"
|
||||
#include "Filesystem.h"
|
||||
#include "RootInode.h"
|
||||
#include "RPCServer.h"
|
||||
|
||||
|
||||
extern fs_volume_ops gNFSv4VolumeOps;
|
||||
@@ -107,7 +108,7 @@ nfs4_mount(fs_volume* volume, const char* device, uint32 flags,
|
||||
return result;
|
||||
}
|
||||
|
||||
Inode* inode = fs->CreateRootInode();
|
||||
Inode* inode = fs->Root();
|
||||
if (inode == NULL) {
|
||||
delete fs;
|
||||
gRPCServerManager->Release(server);
|
||||
@@ -166,7 +167,8 @@ static status_t
|
||||
nfs4_read_fs_info(fs_volume* volume, struct fs_info* info)
|
||||
{
|
||||
Filesystem* fs = reinterpret_cast<Filesystem*>(volume->private_volume);
|
||||
return fs->ReadInfo(info);
|
||||
RootInode* inode = reinterpret_cast<RootInode*>(fs->Root());
|
||||
return inode->ReadInfo(info);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +198,11 @@ nfs4_get_vnode_name(fs_volume* volume, fs_vnode* vnode, char* buffer,
|
||||
static status_t
|
||||
nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
|
||||
{
|
||||
Filesystem* fs = reinterpret_cast<Filesystem*>(volume->private_volume);
|
||||
Inode* inode = reinterpret_cast<Inode*>(vnode->private_node);
|
||||
if (fs->Root() == inode)
|
||||
return B_OK;
|
||||
|
||||
inode->FileSystem()->InoIdMap()->RemoveEntry(inode->ID());
|
||||
delete inode;
|
||||
|
||||
@@ -211,7 +217,11 @@ nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
|
||||
// side are only an attempt to simulate local filesystem. Hence,
|
||||
// this hook is the same as put_vnode().
|
||||
|
||||
Filesystem* fs = reinterpret_cast<Filesystem*>(volume->private_volume);
|
||||
Inode* inode = reinterpret_cast<Inode*>(vnode->private_node);
|
||||
if (fs->Root() == inode)
|
||||
return B_OK;
|
||||
|
||||
inode->FileSystem()->InoIdMap()->RemoveEntry(inode->ID());
|
||||
delete inode;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user