nfs4: Add rename() hook
This commit is contained in:
@@ -210,6 +210,64 @@ Inode::LookUp(const char* name, ino_t* id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rename may cause some problems if filehandles are volatile and local Inode
|
||||||
|
// object exists for renamed node. It's stored filename will become invalid
|
||||||
|
// and, consequnetly, filehandle restoration will fail. Probably, it will
|
||||||
|
// be much easier to solve this problem if more metadata is cached.
|
||||||
|
status_t
|
||||||
|
Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName)
|
||||||
|
{
|
||||||
|
if (from->fFilesystem != to->fFilesystem)
|
||||||
|
return B_DONT_DO_THAT;
|
||||||
|
|
||||||
|
do {
|
||||||
|
RPC::Server* serv = from->fFilesystem->Server();
|
||||||
|
Request request(serv);
|
||||||
|
RequestBuilder& req = request.Builder();
|
||||||
|
|
||||||
|
req.PutFH(from->fHandle);
|
||||||
|
req.SaveFH();
|
||||||
|
req.PutFH(to->fHandle);
|
||||||
|
req.Rename(fromName, toName);
|
||||||
|
|
||||||
|
status_t result = request.Send();
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
ReplyInterpreter& reply = request.Reply();
|
||||||
|
|
||||||
|
// filehandle has expired
|
||||||
|
if (reply.NFS4Error() == NFS4ERR_FHEXPIRED) {
|
||||||
|
from->_LookUpFilehandle();
|
||||||
|
to->_LookUpFilehandle();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// filesystem has been moved
|
||||||
|
if (reply.NFS4Error() == NFS4ERR_MOVED) {
|
||||||
|
from->fFilesystem->Migrate(from->fHandle, serv);
|
||||||
|
to->fFilesystem->Migrate(to->fHandle, serv);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = reply.PutFH();
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result = reply.SaveFH();
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result = reply.PutFH();
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
|
||||||
|
return reply.Rename();
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Inode::ReadLink(void* buffer, size_t* length)
|
Inode::ReadLink(void* buffer, size_t* length)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ public:
|
|||||||
|
|
||||||
status_t LookUp(const char* name, ino_t* id);
|
status_t LookUp(const char* name, ino_t* id);
|
||||||
status_t ReadLink(void* buffer, size_t* length);
|
status_t ReadLink(void* buffer, size_t* length);
|
||||||
|
static status_t Rename(Inode* from, Inode* to,
|
||||||
|
const char* fromName, const char* toName);
|
||||||
status_t Access(int mode);
|
status_t Access(int mode);
|
||||||
status_t Stat(struct stat* st);
|
status_t Stat(struct stat* st);
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ enum Opcode {
|
|||||||
OpRead = 25,
|
OpRead = 25,
|
||||||
OpReadDir = 26,
|
OpReadDir = 26,
|
||||||
OpReadLink = 27,
|
OpReadLink = 27,
|
||||||
|
OpRename = 29,
|
||||||
OpRenew = 30,
|
OpRenew = 30,
|
||||||
|
OpSaveFH = 32,
|
||||||
OpSetClientID = 35,
|
OpSetClientID = 35,
|
||||||
OpSetClientIDConfirm = 36
|
OpSetClientIDConfirm = 36
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -289,6 +289,26 @@ ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize)
|
|||||||
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ReplyInterpreter::Rename()
|
||||||
|
{
|
||||||
|
status_t res = _OperationError(OpRename);
|
||||||
|
if (res != B_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
fReply->Stream().GetBoolean();
|
||||||
|
fReply->Stream().GetUHyper();
|
||||||
|
fReply->Stream().GetUHyper();
|
||||||
|
|
||||||
|
fReply->Stream().GetBoolean();
|
||||||
|
fReply->Stream().GetUHyper();
|
||||||
|
fReply->Stream().GetUHyper();
|
||||||
|
|
||||||
|
return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
|
ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
|
||||||
{
|
{
|
||||||
@@ -556,7 +576,7 @@ ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x)
|
|||||||
case NFS4ERR_NOENT: return B_ENTRY_NOT_FOUND;
|
case NFS4ERR_NOENT: return B_ENTRY_NOT_FOUND;
|
||||||
case NFS4ERR_IO: return B_IO_ERROR;
|
case NFS4ERR_IO: return B_IO_ERROR;
|
||||||
case NFS4ERR_NXIO: return B_DEVICE_NOT_FOUND;
|
case NFS4ERR_NXIO: return B_DEVICE_NOT_FOUND;
|
||||||
case NFS4ERR_ACCESS: return B_PERMISSION_DENIED;
|
case NFS4ERR_ACCESS: return B_NOT_ALLOWED;
|
||||||
case NFS4ERR_EXIST: return B_FILE_EXISTS;
|
case NFS4ERR_EXIST: return B_FILE_EXISTS;
|
||||||
case NFS4ERR_XDEV: return B_CROSS_DEVICE_LINK;
|
case NFS4ERR_XDEV: return B_CROSS_DEVICE_LINK;
|
||||||
case NFS4ERR_NOTDIR: return B_NOT_A_DIRECTORY;
|
case NFS4ERR_NOTDIR: return B_NOT_A_DIRECTORY;
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ public:
|
|||||||
status_t ReadDir(uint64* cookie, uint64* cookieVerf,
|
status_t ReadDir(uint64* cookie, uint64* cookieVerf,
|
||||||
DirEntry** dirents, uint32* count, bool* eof);
|
DirEntry** dirents, uint32* count, bool* eof);
|
||||||
status_t ReadLink(void* buffer, uint32* size, uint32 maxSize);
|
status_t ReadLink(void* buffer, uint32* size, uint32 maxSize);
|
||||||
|
status_t Rename();
|
||||||
inline status_t Renew();
|
inline status_t Renew();
|
||||||
|
inline status_t SaveFH();
|
||||||
status_t SetClientID(uint64* clientid, uint64* verifier);
|
status_t SetClientID(uint64* clientid, uint64* verifier);
|
||||||
inline status_t SetClientIDConfirm();
|
inline status_t SetClientIDConfirm();
|
||||||
|
|
||||||
@@ -161,6 +163,13 @@ ReplyInterpreter::Renew()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline status_t
|
||||||
|
ReplyInterpreter::SaveFH()
|
||||||
|
{
|
||||||
|
return _OperationError(OpSaveFH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline status_t
|
inline status_t
|
||||||
ReplyInterpreter::SetClientIDConfirm()
|
ReplyInterpreter::SetClientIDConfirm()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -308,6 +308,24 @@ RequestBuilder::ReadLink()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
status_t
|
||||||
RequestBuilder::Renew(uint64 clientId)
|
RequestBuilder::Renew(uint64 clientId)
|
||||||
{
|
{
|
||||||
@@ -325,6 +343,21 @@ RequestBuilder::Renew(uint64 clientId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
status_t
|
||||||
RequestBuilder::SetClientID(const RPC::Server* serv)
|
RequestBuilder::SetClientID(const RPC::Server* serv)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ public:
|
|||||||
uint64 cookieVerf, Attribute* attrs,
|
uint64 cookieVerf, Attribute* attrs,
|
||||||
uint32 attrCount);
|
uint32 attrCount);
|
||||||
status_t ReadLink();
|
status_t ReadLink();
|
||||||
|
status_t Rename(const char* from, const char* to);
|
||||||
status_t Renew(uint64 clientId);
|
status_t Renew(uint64 clientId);
|
||||||
|
status_t SaveFH();
|
||||||
status_t SetClientID(const RPC::Server* serv);
|
status_t SetClientID(const RPC::Server* serv);
|
||||||
status_t SetClientIDConfirm(uint64 id, uint64 ver);
|
status_t SetClientIDConfirm(uint64 id, uint64 ver);
|
||||||
|
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ status_t
|
|||||||
WriteStream::AddString(const char* str, uint32 maxlen)
|
WriteStream::AddString(const char* str, uint32 maxlen)
|
||||||
{
|
{
|
||||||
uint32 len = strlen(str);
|
uint32 len = strlen(str);
|
||||||
uint32 size = min_c(maxlen, len);
|
uint32 size = maxlen == 0 ? len : min_c(maxlen, len);
|
||||||
|
|
||||||
return AddOpaque(str, size);
|
return AddOpaque(str, size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public:
|
|||||||
|
|
||||||
inline status_t AddBoolean(bool x);
|
inline status_t AddBoolean(bool x);
|
||||||
|
|
||||||
status_t AddString(const char* str, uint32 maxlen);
|
status_t AddString(const char* str, uint32 maxlen = 0);
|
||||||
|
|
||||||
status_t AddOpaque(const void* ptr, uint32 size);
|
status_t AddOpaque(const void* ptr, uint32 size);
|
||||||
status_t AddOpaque(const WriteStream& stream);
|
status_t AddOpaque(const WriteStream& stream);
|
||||||
|
|||||||
@@ -212,6 +212,16 @@ nfs4_read_symlink(fs_volume* volume, fs_vnode* link, char* buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName,
|
||||||
|
fs_vnode* toDir, const char* toName)
|
||||||
|
{
|
||||||
|
Inode* fromInode = reinterpret_cast<Inode*>(fromDir->private_node);
|
||||||
|
Inode* toInode = reinterpret_cast<Inode*>(toDir->private_node);
|
||||||
|
return Inode::Rename(fromInode, toInode, fromName, toName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
nfs4_access(fs_volume* volume, fs_vnode* vnode, int mode)
|
nfs4_access(fs_volume* volume, fs_vnode* vnode, int mode)
|
||||||
{
|
{
|
||||||
@@ -425,7 +435,7 @@ fs_vnode_ops gNFSv4VnodeOps = {
|
|||||||
|
|
||||||
NULL, // link()
|
NULL, // link()
|
||||||
NULL, // unlink()
|
NULL, // unlink()
|
||||||
NULL, // rename()
|
nfs4_rename,
|
||||||
|
|
||||||
nfs4_access,
|
nfs4_access,
|
||||||
nfs4_read_stat,
|
nfs4_read_stat,
|
||||||
|
|||||||
Reference in New Issue
Block a user