nfs4: Add write() hook

This commit is contained in:
Pawel Dziepak
2012-06-29 02:15:07 +02:00
parent 20bd3ee165
commit e36a54427c
8 changed files with 140 additions and 2 deletions
@@ -1019,6 +1019,74 @@ Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
}
status_t
Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
size_t length)
{
uint32 size = 0;
uint32 len = 0;
const char* buffer = reinterpret_cast<const char*>(_buffer);
while (size < length) {
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.Write(cookie->fStateId, cookie->fStateSeq, buffer + size,
pos + size, length - size);
status_t result = request.Send(cookie);
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
// filehandle has expired
if (reply.NFS4Error() == NFS4ERR_FHEXPIRED) {
_LookUpFilehandle();
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,
B_SYSTEM_TIMEBASE, B_RELATIVE_TIMEOUT);
continue;
}
// server has rebooted, reclaim share and try again
if (reply.NFS4Error() == NFS4ERR_STALE_CLIENTID ||
reply.NFS4Error() == NFS4ERR_STALE_STATEID) {
fFilesystem->NFSServer()->ServerRebooted(cookie->fClientId);
continue;
}
result = reply.PutFH();
if (result != B_OK)
return result;
result = reply.Write(&len);
if (result != B_OK)
return result;
size += len;
break;
} while (true);
}
return B_OK;
}
status_t
Inode::OpenDir(OpenDirCookie* cookie)
{
@@ -49,6 +49,8 @@ public:
status_t Close(OpenFileCookie* 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 OpenDir(OpenDirCookie* cookie);
status_t ReadDir(void* buffer, uint32 size,
@@ -42,7 +42,8 @@ enum Opcode {
OpSaveFH = 32,
OpSetClientID = 35,
OpSetClientIDConfirm = 36,
OpVerify = 37
OpVerify = 37,
OpWrite = 38
};
enum Access {
@@ -171,6 +172,12 @@ enum OpenFlags {
OPEN4_RESULT_LOCKTYPE_POSIX = 4
};
enum WriteStable {
UNSTABLE4 = 0,
DATA_SYNC4 = 1,
FILE_SYNC4 = 2
};
enum Errors {
NFS4_OK = 0,
NFS4ERR_PERM = 1,
@@ -372,6 +372,21 @@ ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
}
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)
{
@@ -87,6 +87,7 @@ public:
status_t SetClientID(uint64* clientid, uint64* verifier);
inline status_t SetClientIDConfirm();
inline status_t Verify();
status_t Write(uint32* size);
private:
void _ParseHeader();
@@ -500,6 +500,30 @@ RequestBuilder::Verify(AttrValue* attr, uint32 count)
}
status_t
RequestBuilder::Write(const uint32* id, uint32 stateSeq, const void* buffer,
uint64 pos, uint32 len)
{
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(FILE_SYNC4);
fRequest->Stream().AddOpaque(buffer, len);
fOpCount++;
return B_OK;
}
RPC::Call*
RequestBuilder::Request()
{
@@ -58,6 +58,9 @@ public:
status_t SetClientID(const RPC::Server* serv);
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);
RPC::Call* Request();
@@ -353,6 +353,24 @@ nfs4_read(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
}
static status_t
nfs4_write(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
const void* buffer, size_t* length)
{
Inode* inode = reinterpret_cast<Inode*>(vnode->private_node);
if (inode->Type() == S_IFDIR)
return B_IS_A_DIRECTORY;
if (inode->Type() == S_IFLNK)
return B_BAD_VALUE;
OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
return inode->Write(cookie, pos, buffer, *length);
}
static status_t
nfs4_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
{
@@ -500,7 +518,7 @@ fs_vnode_ops gNFSv4VnodeOps = {
nfs4_close,
nfs4_free_cookie,
nfs4_read,
NULL, // write,
nfs4_write,
/* directory operations */
NULL, // create_dir()