nfs4: Put file and dir specific code in separate files

This commit is contained in:
Pawel Dziepak 2012-06-28 23:13:45 +02:00
parent 2f03ff09af
commit 2a73e4c578
5 changed files with 700 additions and 659 deletions

View File

@ -9,7 +9,6 @@
#include "Inode.h"
#include <dirent.h>
#include <string.h>
#include <NodeMonitor.h>
@ -651,661 +650,6 @@ Inode::WriteStat(const struct stat* st, uint32 mask)
}
static status_t
sConfirmOpen(Filesystem* fs, Filehandle& fh, OpenFileCookie* cookie)
{
Request request(fs->Server());
RequestBuilder& req = request.Builder();
req.PutFH(fh);
req.OpenConfirm(cookie->fSequence++, cookie->fStateId,
cookie->fStateSeq);
status_t result = request.Send();
if (result != B_OK) {
fs->RemoveOpenFile(cookie);
return result;
}
ReplyInterpreter& reply = request.Reply();
reply.PutFH();
result = reply.OpenConfirm(&cookie->fStateSeq);
if (result != B_OK) {
fs->RemoveOpenFile(cookie);
return result;
}
return B_OK;
}
status_t
Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
ino_t* id)
{
bool confirm;
status_t result;
cookie->fMode = mode;
cookie->fSequence = 0;
cookie->fLocks = NULL;
Filehandle fh;
do {
cookie->fClientId = fFilesystem->NFSServer()->ClientId();
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
cookie->fOwnerId = atomic_add64(&cookie->fLastOwnerId, 1);
req.PutFH(fHandle);
AttrValue cattr[2];
uint32 i = 0;
if ((mode & O_TRUNC) == O_TRUNC) {
cattr[i].fAttribute = FATTR4_SIZE;
cattr[i].fFreePointer = false;
cattr[i].fData.fValue64 = 0;
i++;
}
cattr[i].fAttribute = FATTR4_MODE;
cattr[i].fFreePointer = false;
cattr[i].fData.fValue32 = perms;
req.Open(CLAIM_NULL, cookie->fSequence++, sModeToAccess(mode),
cookie->fClientId, OPEN4_CREATE, cookie->fOwnerId, name, cattr,
i + 1, (mode & O_EXCL) == O_EXCL);
req.GetFH();
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
Attribute attr[] = { FATTR4_FILEID };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
}
result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
reply.Open(cookie->fStateId, &cookie->fStateSeq, &confirm);
reply.GetFH(&fh);
uint64 fileId;
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
AttrValue* values;
uint32 count;
result = reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
fileId = values[1].fData.fValue64;
delete[] values;
} else
fileId = fFilesystem->AllocFileId();
*id = _FileIdToInoT(fileId);
FileInfo fi;
fi.fFileId = fileId;
fi.fFH = fh;
fi.fParent = fHandle;
fi.fName = strdup(name);
char* path = reinterpret_cast<char*>(malloc(strlen(name) + 2 +
strlen(fPath)));
strcpy(path, fPath);
strcat(path, "/");
strcat(path, name);
fi.fPath = path;
fFilesystem->InoIdMap()->AddEntry(fi, *id);
cookie->fFilesystem = fFilesystem;
cookie->fHandle = fh;
break;
} while (true);
fFilesystem->AddOpenFile(cookie);
if (confirm)
return sConfirmOpen(fFilesystem, fh, cookie);
else
return B_OK;
}
status_t
Inode::Open(int mode, OpenFileCookie* cookie)
{
bool confirm;
status_t result;
cookie->fFilesystem = fFilesystem;
cookie->fHandle = fHandle;
cookie->fMode = mode;
cookie->fSequence = 0;
cookie->fLocks = NULL;
do {
cookie->fClientId = fFilesystem->NFSServer()->ClientId();
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
cookie->fOwnerId = atomic_add64(&cookie->fLastOwnerId, 1);
// Since we are opening the file using a pair (parentFH, name) we
// need to check for race conditions.
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
req.PutFH(fParentFH);
req.LookUp(fName);
AttrValue attr;
attr.fAttribute = FATTR4_FILEID;
attr.fFreePointer = false;
attr.fData.fValue64 = fFileId;
req.Verify(&attr, 1);
} else if (fFilesystem->ExpireType() == FH4_PERSISTENT) {
req.PutFH(fParentFH);
req.LookUp(fName);
AttrValue attr;
attr.fAttribute = FATTR4_FILEHANDLE;
attr.fFreePointer = true;
attr.fData.fPointer = malloc(sizeof(fHandle));
memcpy(attr.fData.fPointer, &fHandle, sizeof(fHandle));
req.Verify(&attr, 1);
}
req.PutFH(fParentFH);
if ((mode & O_TRUNC) == O_TRUNC) {
AttrValue attr;
attr.fAttribute = FATTR4_SIZE;
attr.fFreePointer = false;
attr.fData.fValue64 = 0;
req.Open(CLAIM_NULL, cookie->fSequence++, sModeToAccess(mode),
cookie->fClientId, OPEN4_CREATE, cookie->fOwnerId, fName, &attr,
1, false);
} else
req.Open(CLAIM_NULL, cookie->fSequence++, sModeToAccess(mode),
cookie->fClientId, OPEN4_NOCREATE, cookie->fOwnerId, fName);
result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
// Verify if the file we want to open is the file this Inode
// represents.
if (fFilesystem->IsAttrSupported(FATTR4_FILEID) ||
fFilesystem->ExpireType() == FH4_PERSISTENT) {
reply.PutFH();
result = reply.LookUp();
if (result != B_OK)
return result;
result = reply.Verify();
if (result != B_OK && reply.NFS4Error() == NFS4ERR_NOT_SAME)
return B_ENTRY_NOT_FOUND;
else if (result != B_OK)
return result;
}
reply.PutFH();
result = reply.Open(cookie->fStateId, &cookie->fStateSeq, &confirm);
if (result != B_OK)
return result;
break;
} while (true);
fFilesystem->AddOpenFile(cookie);
if (confirm)
return sConfirmOpen(fFilesystem, fHandle, cookie);
else
return B_OK;
}
status_t
Inode::Close(OpenFileCookie* cookie)
{
fFilesystem->RemoveOpenFile(cookie);
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.Close(cookie->fSequence++, cookie->fStateId,
cookie->fStateSeq);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv, cookie))
continue;
reply.PutFH();
result = reply.Close();
if (result != B_OK)
return result;
return B_OK;
} while (true);
return B_OK;
}
status_t
Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
{
bool eof = false;
uint32 size = 0;
uint32 len = 0;
while (size < *_length && !eof) {
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.Read(cookie->fStateId, cookie->fStateSeq, pos + size,
*_length - size);
status_t result = request.Send(cookie);
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv, cookie))
continue;
reply.PutFH();
result = reply.Read(reinterpret_cast<char*>(buffer) + size, &len,
&eof);
if (result != B_OK)
return result;
size += len;
break;
} while (true);
}
*_length = size;
return B_OK;
}
status_t
Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
size_t *_length)
{
uint32 size = 0;
uint32 len = 0;
uint64 fileSize;
const char* buffer = reinterpret_cast<const char*>(_buffer);
while (size < *_length) {
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
if (size == 0 && (cookie->fMode & O_APPEND) == O_APPEND) {
struct stat st;
status_t result = Stat(&st);
if (result != B_OK)
return result;
fileSize = st.st_size;
pos = fileSize;
}
req.PutFH(fHandle);
if ((cookie->fMode & O_APPEND) == O_APPEND) {
AttrValue attr;
attr.fAttribute = FATTR4_SIZE;
attr.fFreePointer = false;
attr.fData.fValue64 = fileSize + size;
req.Verify(&attr, 1);
}
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();
// append: race condition
if (reply.NFS4Error() == NFS4ERR_NOT_SAME) {
if (size == 0)
continue;
else {
*_length = size;
return B_OK;
}
}
if (_HandleErrors(reply.NFS4Error(), serv, cookie))
continue;
reply.PutFH();
if ((cookie->fMode & O_APPEND) == O_APPEND) {
result = reply.Verify();
if (result != B_OK)
return result;
}
result = reply.Write(&len);
if (result != B_OK)
return result;
size += len;
break;
} while (true);
}
*_length = size;
return B_OK;
}
status_t
Inode::CreateDir(const char* name, int mode)
{
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
AttrValue attr;
attr.fAttribute = FATTR4_MODE;
attr.fFreePointer = false;
attr.fData.fValue32 = mode;
req.Create(NF4DIR, name, &attr, 1);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
return reply.Create();
} while (true);
}
status_t
Inode::OpenDir(OpenDirCookie* cookie)
{
if (fType != NF4DIR)
return B_NOT_A_DIRECTORY;
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)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
uint32 allowed;
result = reply.Access(NULL, &allowed);
if (result != B_OK)
return result;
if (allowed & ACCESS4_READ != ACCESS4_READ)
return B_PERMISSION_DENIED;
cookie->fFilesystem = fFilesystem;
cookie->fCookie = 0;
cookie->fCookieVerf = 2;
return B_OK;
} while (true);
}
status_t
Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
bool* eof)
{
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
req.ReadDir(*count, cookie->fCookie, cookie->fCookieVerf, attr,
sizeof(attr) / sizeof(Attribute));
status_t result = request.Send(cookie);
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
return reply.ReadDir(&cookie->fCookie, &cookie->fCookieVerf, dirents,
count, eof);
} while (true);
}
status_t
Inode::_FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
uint32 size)
{
uint32 nameSize = strlen(name);
const uint32 entSize = sizeof(struct dirent);
if (pos + entSize + nameSize > size)
return B_BUFFER_OVERFLOW;
de->d_dev = fFilesystem->DevId();
de->d_ino = id;
de->d_reclen = entSize + nameSize;
if (de->d_reclen % 8 != 0)
de->d_reclen += 8 - de->d_reclen % 8;
strcpy(de->d_name, name);
return B_OK;
}
status_t
Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
{
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.LookUpUp();
req.GetFH();
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
Attribute attr[] = { FATTR4_FILEID };
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();
result = reply.LookUpUp();
if (result != B_OK)
return result;
Filehandle fh;
reply.GetFH(&fh);
uint64 fileId;
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
AttrValue* values;
uint32 count;
reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
fileId = values[0].fData.fValue64;
delete[] values;
} else
fileId = fFilesystem->AllocFileId();
return _FillDirEntry(de, _FileIdToInoT(fileId), "..", pos, size);
} while (true);
}
// TODO: Currently inode numbers returned by ReadDir are virtually random.
// Apparently Haiku does not use that information (contrary to inode number
// returned by LookUp) so fixing it can wait until directory caches are
// implemented.
// When directories are cached client should store inode numbers it assigned
// to directroy entries and use them consequently.
status_t
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
OpenDirCookie* cookie)
{
uint32 count = 0;
uint32 pos = 0;
uint32 this_count;
bool eof = false;
char* buffer = reinterpret_cast<char*>(_buffer);
if (cookie->fCookie == 0 && cookie->fCookieVerf == 2 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
_FillDirEntry(de, fFileId, ".", pos, size);
pos += de->d_reclen;
count++;
cookie->fCookieVerf--;
}
if (cookie->fCookie == 0 && cookie->fCookieVerf == 1 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
if (strcmp(fName, "/"))
_ReadDirUp(de, pos, size);
else
_FillDirEntry(de, _FileIdToInoT(fFileId), "..", pos, size);
pos += de->d_reclen;
count++;
cookie->fCookieVerf--;
}
bool overflow = false;
while (count < *_count && !eof) {
this_count = *_count - count;
DirEntry* dirents;
status_t result = _ReadDirOnce(&dirents, &this_count, cookie, &eof);
if (result != B_OK)
return result;
uint32 i, entries = 0;
for (i = 0; i < min_c(this_count, *_count - count); i++) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
// FATTR4_FSID is mandatory
void* data = dirents[i].fAttrs[0].fData.fPointer;
FilesystemId* fsid = reinterpret_cast<FilesystemId*>(data);
if (*fsid != fFilesystem->FsId())
continue;
ino_t id;
if (dirents[i].fAttrCount == 2)
id = _FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
else
id = _FileIdToInoT(fFilesystem->AllocFileId());
const char* name = dirents[i].fName;
if (_FillDirEntry(de, id, name, pos, size) == B_BUFFER_OVERFLOW) {
eof = true;
overflow = true;
break;
}
pos += de->d_reclen;
entries++;
}
delete[] dirents;
count += entries;
}
if (count == 0 && overflow)
return B_BUFFER_OVERFLOW;
*_count = count;
return B_OK;
}
status_t
Inode::TestLock(OpenFileCookie* cookie, struct flock* lock)
{
@ -1501,7 +845,6 @@ Inode::ReleaseAllLocks(OpenFileCookie* cookie)
}
bool
Inode::_HandleErrors(uint32 nfs4Error, RPC::Server* serv,
OpenFileCookie* cookie)

View File

@ -71,13 +71,13 @@ public:
protected:
Inode();
bool _HandleErrors(uint32 nfs4Error,
RPC::Server* serv,
OpenFileCookie* cookie = NULL);
status_t _LookUpFilehandle();
status_t _ConfirmOpen(OpenFileCookie* cookie);
status_t _ReadDirOnce(DirEntry** dirents, uint32* count,
OpenDirCookie* cookie, bool* eof);
status_t _FillDirEntry(struct dirent* de, ino_t id,

View File

@ -0,0 +1,283 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Inode.h"
#include <dirent.h>
#include <string.h>
#include "Request.h"
#include "RootInode.h"
status_t
Inode::CreateDir(const char* name, int mode)
{
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
AttrValue attr;
attr.fAttribute = FATTR4_MODE;
attr.fFreePointer = false;
attr.fData.fValue32 = mode;
req.Create(NF4DIR, name, &attr, 1);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
return reply.Create();
} while (true);
}
status_t
Inode::OpenDir(OpenDirCookie* cookie)
{
if (fType != NF4DIR)
return B_NOT_A_DIRECTORY;
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)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
uint32 allowed;
result = reply.Access(NULL, &allowed);
if (result != B_OK)
return result;
if (allowed & ACCESS4_READ != ACCESS4_READ)
return B_PERMISSION_DENIED;
cookie->fFilesystem = fFilesystem;
cookie->fCookie = 0;
cookie->fCookieVerf = 2;
return B_OK;
} while (true);
}
status_t
Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
bool* eof)
{
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
req.ReadDir(*count, cookie->fCookie, cookie->fCookieVerf, attr,
sizeof(attr) / sizeof(Attribute));
status_t result = request.Send(cookie);
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
return reply.ReadDir(&cookie->fCookie, &cookie->fCookieVerf, dirents,
count, eof);
} while (true);
}
status_t
Inode::_FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
uint32 size)
{
uint32 nameSize = strlen(name);
const uint32 entSize = sizeof(struct dirent);
if (pos + entSize + nameSize > size)
return B_BUFFER_OVERFLOW;
de->d_dev = fFilesystem->DevId();
de->d_ino = id;
de->d_reclen = entSize + nameSize;
if (de->d_reclen % 8 != 0)
de->d_reclen += 8 - de->d_reclen % 8;
strcpy(de->d_name, name);
return B_OK;
}
status_t
Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
{
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.LookUpUp();
req.GetFH();
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
Attribute attr[] = { FATTR4_FILEID };
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();
result = reply.LookUpUp();
if (result != B_OK)
return result;
Filehandle fh;
reply.GetFH(&fh);
uint64 fileId;
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
AttrValue* values;
uint32 count;
reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
fileId = values[0].fData.fValue64;
delete[] values;
} else
fileId = fFilesystem->AllocFileId();
return _FillDirEntry(de, _FileIdToInoT(fileId), "..", pos, size);
} while (true);
}
// TODO: Currently inode numbers returned by ReadDir are virtually random.
// Apparently Haiku does not use that information (contrary to inode number
// returned by LookUp) so fixing it can wait until directory caches are
// implemented.
// When directories are cached client should store inode numbers it assigned
// to directroy entries and use them consequently.
status_t
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
OpenDirCookie* cookie)
{
uint32 count = 0;
uint32 pos = 0;
uint32 this_count;
bool eof = false;
char* buffer = reinterpret_cast<char*>(_buffer);
if (cookie->fCookie == 0 && cookie->fCookieVerf == 2 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
_FillDirEntry(de, fFileId, ".", pos, size);
pos += de->d_reclen;
count++;
cookie->fCookieVerf--;
}
if (cookie->fCookie == 0 && cookie->fCookieVerf == 1 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
if (strcmp(fName, "/"))
_ReadDirUp(de, pos, size);
else
_FillDirEntry(de, _FileIdToInoT(fFileId), "..", pos, size);
pos += de->d_reclen;
count++;
cookie->fCookieVerf--;
}
bool overflow = false;
while (count < *_count && !eof) {
this_count = *_count - count;
DirEntry* dirents;
status_t result = _ReadDirOnce(&dirents, &this_count, cookie, &eof);
if (result != B_OK)
return result;
uint32 i, entries = 0;
for (i = 0; i < min_c(this_count, *_count - count); i++) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
// FATTR4_FSID is mandatory
void* data = dirents[i].fAttrs[0].fData.fPointer;
FilesystemId* fsid = reinterpret_cast<FilesystemId*>(data);
if (*fsid != fFilesystem->FsId())
continue;
ino_t id;
if (dirents[i].fAttrCount == 2)
id = _FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
else
id = _FileIdToInoT(fFilesystem->AllocFileId());
const char* name = dirents[i].fName;
if (_FillDirEntry(de, id, name, pos, size) == B_BUFFER_OVERFLOW) {
eof = true;
overflow = true;
break;
}
pos += de->d_reclen;
entries++;
}
delete[] dirents;
count += entries;
}
if (count == 0 && overflow)
return B_BUFFER_OVERFLOW;
*_count = count;
return B_OK;
}

View File

@ -0,0 +1,413 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "Inode.h"
#include <string.h>
#include <NodeMonitor.h>
#include "Request.h"
status_t
Inode::_ConfirmOpen(OpenFileCookie* cookie)
{
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.OpenConfirm(cookie->fSequence++, cookie->fStateId,
cookie->fStateSeq);
status_t result = request.Send();
if (result != B_OK) {
fFilesystem->RemoveOpenFile(cookie);
return result;
}
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
result = reply.OpenConfirm(&cookie->fStateSeq);
if (result != B_OK) {
fFilesystem->RemoveOpenFile(cookie);
return result;
}
break;
} while (true);
return B_OK;
}
status_t
Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
ino_t* id)
{
bool confirm;
status_t result;
cookie->fMode = mode;
cookie->fSequence = 0;
cookie->fLocks = NULL;
Filehandle fh;
do {
cookie->fClientId = fFilesystem->NFSServer()->ClientId();
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
cookie->fOwnerId = atomic_add64(&cookie->fLastOwnerId, 1);
req.PutFH(fHandle);
AttrValue cattr[2];
uint32 i = 0;
if ((mode & O_TRUNC) == O_TRUNC) {
cattr[i].fAttribute = FATTR4_SIZE;
cattr[i].fFreePointer = false;
cattr[i].fData.fValue64 = 0;
i++;
}
cattr[i].fAttribute = FATTR4_MODE;
cattr[i].fFreePointer = false;
cattr[i].fData.fValue32 = perms;
req.Open(CLAIM_NULL, cookie->fSequence++, sModeToAccess(mode),
cookie->fClientId, OPEN4_CREATE, cookie->fOwnerId, name, cattr,
i + 1, (mode & O_EXCL) == O_EXCL);
req.GetFH();
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
Attribute attr[] = { FATTR4_FILEID };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
}
result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
reply.PutFH();
reply.Open(cookie->fStateId, &cookie->fStateSeq, &confirm);
reply.GetFH(&fh);
uint64 fileId;
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
AttrValue* values;
uint32 count;
result = reply.GetAttr(&values, &count);
if (result != B_OK)
return result;
fileId = values[1].fData.fValue64;
delete[] values;
} else
fileId = fFilesystem->AllocFileId();
*id = _FileIdToInoT(fileId);
FileInfo fi;
fi.fFileId = fileId;
fi.fFH = fh;
fi.fParent = fHandle;
fi.fName = strdup(name);
char* path = reinterpret_cast<char*>(malloc(strlen(name) + 2 +
strlen(fPath)));
strcpy(path, fPath);
strcat(path, "/");
strcat(path, name);
fi.fPath = path;
fFilesystem->InoIdMap()->AddEntry(fi, *id);
cookie->fFilesystem = fFilesystem;
cookie->fHandle = fh;
break;
} while (true);
fFilesystem->AddOpenFile(cookie);
if (confirm)
return _ConfirmOpen(cookie);
else
return B_OK;
}
status_t
Inode::Open(int mode, OpenFileCookie* cookie)
{
bool confirm;
status_t result;
cookie->fFilesystem = fFilesystem;
cookie->fHandle = fHandle;
cookie->fMode = mode;
cookie->fSequence = 0;
cookie->fLocks = NULL;
do {
cookie->fClientId = fFilesystem->NFSServer()->ClientId();
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
cookie->fOwnerId = atomic_add64(&cookie->fLastOwnerId, 1);
// Since we are opening the file using a pair (parentFH, name) we
// need to check for race conditions.
if (fFilesystem->IsAttrSupported(FATTR4_FILEID)) {
req.PutFH(fParentFH);
req.LookUp(fName);
AttrValue attr;
attr.fAttribute = FATTR4_FILEID;
attr.fFreePointer = false;
attr.fData.fValue64 = fFileId;
req.Verify(&attr, 1);
} else if (fFilesystem->ExpireType() == FH4_PERSISTENT) {
req.PutFH(fParentFH);
req.LookUp(fName);
AttrValue attr;
attr.fAttribute = FATTR4_FILEHANDLE;
attr.fFreePointer = true;
attr.fData.fPointer = malloc(sizeof(fHandle));
memcpy(attr.fData.fPointer, &fHandle, sizeof(fHandle));
req.Verify(&attr, 1);
}
req.PutFH(fParentFH);
if ((mode & O_TRUNC) == O_TRUNC) {
AttrValue attr;
attr.fAttribute = FATTR4_SIZE;
attr.fFreePointer = false;
attr.fData.fValue64 = 0;
req.Open(CLAIM_NULL, cookie->fSequence++, sModeToAccess(mode),
cookie->fClientId, OPEN4_CREATE, cookie->fOwnerId, fName, &attr,
1, false);
} else
req.Open(CLAIM_NULL, cookie->fSequence++, sModeToAccess(mode),
cookie->fClientId, OPEN4_NOCREATE, cookie->fOwnerId, fName);
result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv))
continue;
// Verify if the file we want to open is the file this Inode
// represents.
if (fFilesystem->IsAttrSupported(FATTR4_FILEID) ||
fFilesystem->ExpireType() == FH4_PERSISTENT) {
reply.PutFH();
result = reply.LookUp();
if (result != B_OK)
return result;
result = reply.Verify();
if (result != B_OK && reply.NFS4Error() == NFS4ERR_NOT_SAME)
return B_ENTRY_NOT_FOUND;
else if (result != B_OK)
return result;
}
reply.PutFH();
result = reply.Open(cookie->fStateId, &cookie->fStateSeq, &confirm);
if (result != B_OK)
return result;
break;
} while (true);
fFilesystem->AddOpenFile(cookie);
if (confirm)
return _ConfirmOpen(cookie);
else
return B_OK;
}
status_t
Inode::Close(OpenFileCookie* cookie)
{
fFilesystem->RemoveOpenFile(cookie);
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.Close(cookie->fSequence++, cookie->fStateId,
cookie->fStateSeq);
status_t result = request.Send();
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv, cookie))
continue;
reply.PutFH();
result = reply.Close();
if (result != B_OK)
return result;
return B_OK;
} while (true);
return B_OK;
}
status_t
Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
{
bool eof = false;
uint32 size = 0;
uint32 len = 0;
while (size < *_length && !eof) {
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
req.PutFH(fHandle);
req.Read(cookie->fStateId, cookie->fStateSeq, pos + size,
*_length - size);
status_t result = request.Send(cookie);
if (result != B_OK)
return result;
ReplyInterpreter& reply = request.Reply();
if (_HandleErrors(reply.NFS4Error(), serv, cookie))
continue;
reply.PutFH();
result = reply.Read(reinterpret_cast<char*>(buffer) + size, &len,
&eof);
if (result != B_OK)
return result;
size += len;
break;
} while (true);
}
*_length = size;
return B_OK;
}
status_t
Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
size_t *_length)
{
uint32 size = 0;
uint32 len = 0;
uint64 fileSize;
const char* buffer = reinterpret_cast<const char*>(_buffer);
while (size < *_length) {
do {
RPC::Server* serv = fFilesystem->Server();
Request request(serv);
RequestBuilder& req = request.Builder();
if (size == 0 && (cookie->fMode & O_APPEND) == O_APPEND) {
struct stat st;
status_t result = Stat(&st);
if (result != B_OK)
return result;
fileSize = st.st_size;
pos = fileSize;
}
req.PutFH(fHandle);
if ((cookie->fMode & O_APPEND) == O_APPEND) {
AttrValue attr;
attr.fAttribute = FATTR4_SIZE;
attr.fFreePointer = false;
attr.fData.fValue64 = fileSize + size;
req.Verify(&attr, 1);
}
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();
// append: race condition
if (reply.NFS4Error() == NFS4ERR_NOT_SAME) {
if (size == 0)
continue;
else {
*_length = size;
return B_OK;
}
}
if (_HandleErrors(reply.NFS4Error(), serv, cookie))
continue;
reply.PutFH();
if ((cookie->fMode & O_APPEND) == O_APPEND) {
result = reply.Verify();
if (result != B_OK)
return result;
}
result = reply.Write(&len);
if (result != B_OK)
return result;
size += len;
break;
} while (true);
}
*_length = size;
return B_OK;
}

View File

@ -8,6 +8,8 @@ KernelAddon nfs4 :
Connection.cpp
Filesystem.cpp
Inode.cpp
InodeDir.cpp
InodeRegular.cpp
kernel_interface.cpp
NFS4Server.cpp
ReplyInterpreter.cpp