nfs4: Add read_dir() hook

This commit is contained in:
Pawel Dziepak 2012-05-31 18:06:21 +02:00
parent 821f13b077
commit 4b48348f97
10 changed files with 347 additions and 67 deletions

View File

@ -30,7 +30,8 @@ Filesystem::~Filesystem()
status_t
Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath)
Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath,
dev_t id)
{
RequestBuilder req(ProcCompound);
req.PutRootFH();
@ -97,14 +98,14 @@ Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath)
return result;
if (count != 1 || values[0].fAttribute != FATTR4_FH_EXPIRE_TYPE) {
delete values;
delete[] values;
return B_BAD_VALUE;
}
// Currently, only persistent filehandles are supported. That will be
// changed soon.
if (values[0].fData.fValue32 != FH4_PERSISTENT) {
delete values;
delete[] values;
return B_UNSUPPORTED;
}
@ -112,8 +113,9 @@ Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath)
fs->fFHExpiryType = values[0].fData.fValue32;
memcpy(&fs->fRootFH, &fh, sizeof(Filehandle));
fs->fServer = serv;
fs->fDevId = id;
delete values;
delete[] values;
*pfs = fs;
@ -124,6 +126,6 @@ Filesystem::Mount(Filesystem** pfs, RPC::Server* serv, const char* fsPath)
Inode*
Filesystem::CreateRootInode()
{
return new(std::nothrow)Inode(this, fRootFH);
return new(std::nothrow)Inode(this, fRootFH, NULL);
}

View File

@ -18,7 +18,7 @@ class Inode;
class Filesystem {
public:
static status_t Mount(Filesystem** pfs, RPC::Server* serv,
const char* path);
const char* path, dev_t id);
~Filesystem();
Inode* CreateRootInode();
@ -27,6 +27,7 @@ public:
inline RPC::Server* Server();
inline uint64 GetId();
inline dev_t DevId() const;
private:
Filesystem();
@ -37,6 +38,7 @@ private:
RPC::Server* fServer;
vint64 fId;
dev_t fDevId;
};
@ -54,5 +56,12 @@ Filesystem::GetId()
}
inline dev_t
Filesystem::DevId() const
{
return fDevId;
}
#endif // FILESYSTEM_H

View File

@ -9,6 +9,7 @@
#include "Inode.h"
#include <dirent.h>
#include <string.h>
#include "ReplyInterpreter.h"
@ -17,9 +18,10 @@
// Creating Inode object from Filehandle probably is not a good idea when
// filehandles are volatile.
Inode::Inode(Filesystem* fs, const Filehandle &fh)
Inode::Inode(Filesystem* fs, const Filehandle &fh, Inode* parent)
:
fFilesystem(fs)
fFilesystem(fs),
fParent(parent)
{
memcpy(&fHandle, &fh, sizeof(fh));
@ -53,7 +55,7 @@ Inode::Inode(Filesystem* fs, const Filehandle &fh)
// FATTR4_TYPE is mandatory
fType = values[0].fData.fValue32;
delete values;
delete[] values;
}
@ -82,8 +84,10 @@ Inode::Stat(struct stat* st)
return result;
// FATTR4_SIZE is mandatory
if (count < 1 || values[0].fAttribute != FATTR4_SIZE)
if (count < 1 || values[0].fAttribute != FATTR4_SIZE) {
delete[] values;
return B_BAD_VALUE;
}
st->st_size = values[0].fData.fValue64;
uint32 next = 1;
@ -103,7 +107,7 @@ Inode::Stat(struct stat* st)
st->st_uid = 0;
st->st_gid = 0;
delete values;
delete[] values;
return B_OK;
}
@ -135,6 +139,126 @@ Inode::OpenDir(uint64* cookie)
if (allowed & ACCESS4_READ != ACCESS4_READ)
return B_PERMISSION_DENIED;
cookie[0] = 0;
cookie[1] = 2;
return B_OK;
}
status_t
Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, uint64* cookie,
bool* eof)
{
RequestBuilder req(ProcCompound);
req.PutFH(fHandle);
Attribute attr[] = { FATTR4_FILEID };
req.ReadDir(*count, cookie, attr, sizeof(attr) / sizeof(Attribute));
RPC::Reply *rpl;
fFilesystem->Server()->SendCall(req.Request(), &rpl);
ReplyInterpreter reply(rpl);
status_t result;
result = reply.PutFH();
if (result != B_OK)
return result;
return reply.ReadDir(cookie, dirents, count, eof);
}
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::ReadDir(void* _buffer, uint32 size, uint32* _count, uint64* cookie)
{
uint32 count = 0;
uint32 pos = 0;
uint32 this_count;
bool eof = false;
char* buffer = reinterpret_cast<char*>(_buffer);
if (cookie[0] == 0 && cookie[1] == 2 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
_FillDirEntry(de, fFileId, ".", pos, size);
pos += de->d_reclen;
count++;
cookie[1]--;
}
if (cookie[0] == 0 && cookie[1] == 1 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
if (fParent != NULL)
_FillDirEntry(de, fParent->fFileId, "..", pos, size);
else
_FillDirEntry(de, fFileId, "..", pos, size);
pos += de->d_reclen;
count++;
cookie[1]--;
}
while (count < *_count && !eof) {
this_count = *_count;
DirEntry* dirents;
status_t result = _ReadDirOnce(&dirents, &this_count, cookie, &eof);
if (result != B_OK)
return result;
uint32 i;
for (i = 0; i < this_count; i++) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
ino_t id;
if (dirents[i].fAttrCount == 1)
id = _FileIdToInoT(dirents[i].fAttrs[0].fData.fValue64);
else
id = _FileIdToInoT(fFilesystem->GetId());
const char* name = dirents[i].fName;
if (_FillDirEntry(de, id, name, pos, size) == B_BUFFER_OVERFLOW) {
eof = true;
break;
}
pos += de->d_reclen;
}
delete[] dirents;
count += i;
}
if (count == 0 && this_count > 0)
return B_BUFFER_OVERFLOW;
*_count = count;
return B_OK;
}

View File

@ -15,35 +15,54 @@
#include "Filesystem.h"
#include "NFS4Defs.h"
#include "ReplyInterpreter.h"
class Inode {
public:
Inode(Filesystem* fs, const Filehandle &fh);
Inode(Filesystem* fs, const Filehandle &fh,
Inode* parent);
inline ino_t ID() const;
inline mode_t Type() const;
inline ino_t ID() const;
inline mode_t Type() const;
status_t Stat(struct stat* st);
status_t OpenDir(uint64* cookie);
status_t Stat(struct stat* st);
status_t OpenDir(uint64* cookie);
status_t ReadDir(void* buffer, uint32 size,
uint32* count, uint64* cookie);
private:
uint64 fFileId;
uint32 fType;
status_t _ReadDirOnce(DirEntry** dirents, uint32* count,
uint64* cookie, bool* eof);
status_t _FillDirEntry(struct dirent* de, ino_t id,
const char* name, uint32 pos, uint32 size);
Filehandle fHandle;
Filesystem* fFilesystem;
static inline ino_t _FileIdToInoT(uint64 fileid);
uint64 fFileId;
uint32 fType;
Filehandle fHandle;
Filesystem* fFilesystem;
Inode* fParent;
};
inline ino_t
Inode::_FileIdToInoT(uint64 fileid)
{
if (sizeof(ino_t) >= sizeof(uint64))
return fileid;
else
return (ino_t)fileid ^ (fileid >>
(sizeof(uint64) - sizeof(ino_t)) * 8);
}
inline ino_t
Inode::ID() const
{
if (sizeof(ino_t) >= sizeof(uint64))
return fFileId;
else
return (ino_t)fFileId ^ (fFileId >>
(sizeof(uint64) - sizeof(ino_t)) * 8);
return _FileIdToInoT(fFileId);
}

View File

@ -32,7 +32,8 @@ enum Opcode {
OpGetFH = 10,
OpLookUp = 15,
OpPutFH = 22,
OpPutRootFH = 24
OpPutRootFH = 24,
OpReadDir = 26
};
enum Access {

View File

@ -14,6 +14,22 @@
#include <util/kernel_cpp.h>
DirEntry::DirEntry()
:
fName(NULL),
fAttrs(NULL),
fAttrCount(0)
{
}
DirEntry::~DirEntry()
{
free(const_cast<char*>(fName));
delete[] fAttrs;
}
ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply)
:
fReply(reply)
@ -75,6 +91,74 @@ ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
if (res != B_OK)
return res;
return _DecodeAttrs(fReply->Stream(), attrs, count);
}
status_t
ReplyInterpreter::GetFH(Filehandle* fh)
{
status_t res = _OperationError(OpGetFH);
if (res != B_OK)
return res;
uint32 size;
const void* ptr = fReply->Stream().GetOpaque(&size);
if (ptr == NULL || size > NFS4_FHSIZE)
return B_BAD_VALUE;
if (fh != NULL) {
fh->fSize = size;
memcpy(fh->fFH, ptr, size);
}
return B_OK;
}
status_t
ReplyInterpreter::ReadDir(uint64* cookie, DirEntry** dirents, uint32* _count,
bool* eof)
{
status_t res = _OperationError(OpReadDir);
if (res != B_OK)
return res;
cookie[1] = fReply->Stream().GetUHyper();
bool isNext;
uint32 count = 0;
DirEntry* entries = new(std::nothrow) DirEntry[*_count];
if (entries == NULL)
return B_NO_MEMORY;
isNext = fReply->Stream().GetBoolean();
while (isNext && count <= *_count) {
cookie[0] = fReply->Stream().GetUHyper();
entries[count].fName = fReply->Stream().GetString();
_DecodeAttrs(fReply->Stream(), &entries[count].fAttrs,
&entries[count].fAttrCount);
count++;
isNext = fReply->Stream().GetBoolean();
}
if (!isNext)
*eof = fReply->Stream().GetBoolean();
else
*eof = false;
*_count = count;
*dirents = entries;
return B_OK;
}
status_t
ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
uint32* count)
{
uint32 bcount = fReply->Stream().GetUInt();
uint32 *bitmap = new(std::nothrow) uint32[bcount];
if (bitmap == NULL)
@ -82,17 +166,17 @@ ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
uint32 attr_count = 0;
for (uint32 i = 0; i < bcount; i++) {
bitmap[i] = fReply->Stream().GetUInt();
bitmap[i] = str.GetUInt();
attr_count += sCountBits(bitmap[i]);
}
uint32 size;
const void* ptr = fReply->Stream().GetOpaque(&size);
const void* ptr = str.GetOpaque(&size);
XDR::ReadStream stream(const_cast<void*>(ptr), size);
AttrValue* values = new(std::nothrow) AttrValue[attr_count];
if (values == NULL) {
delete bitmap;
delete[] bitmap;
return B_NO_MEMORY;
}
@ -134,7 +218,7 @@ ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
current++;
}
delete bitmap;
delete[] bitmap;
*count = attr_count;
*attrs = values;
@ -142,27 +226,6 @@ ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
}
status_t
ReplyInterpreter::GetFH(Filehandle* fh)
{
status_t res = _OperationError(OpGetFH);
if (res != B_OK)
return res;
uint32 size;
const void* ptr = fReply->Stream().GetOpaque(&size);
if (ptr == NULL || size > NFS4_FHSIZE)
return B_BAD_VALUE;
if (fh != NULL) {
fh->fSize = size;
memcpy(fh->fFH, ptr, size);
}
return B_OK;
}
status_t
ReplyInterpreter::_OperationError(Opcode op)
{

View File

@ -24,6 +24,15 @@ struct AttrValue {
} fData;
};
struct DirEntry {
const char* fName;
AttrValue* fAttrs;
uint32 fAttrCount;
DirEntry();
~DirEntry();
};
class ReplyInterpreter {
public:
ReplyInterpreter(RPC::Reply* reply);
@ -35,8 +44,12 @@ public:
inline status_t LookUp();
inline status_t PutFH();
inline status_t PutRootFH();
status_t ReadDir(uint64* cookie, DirEntry** dirents,
uint32* count, bool* eof);
private:
status_t _DecodeAttrs(XDR::ReadStream& stream, AttrValue** attrs,
uint32* count);
status_t _OperationError(Opcode op);
static status_t _NFS4ErrorToHaiku(uint32 x);

View File

@ -62,18 +62,7 @@ RequestBuilder::GetAttr(Attribute* attrs, uint32 count)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpGetAttr);
// 2 is safe in NFS4, not in NFS4.1 though
uint32 bitmap[2];
memset(bitmap, 0, sizeof(bitmap));
for (uint32 i = 0; i < count; i++) {
bitmap[attrs[i] / 32] |= 1 << attrs[i] % 32;
}
uint32 bcount = bitmap[1] != 0 ? 2 : 1;
fRequest->Stream().AddUInt(bcount);
for (uint32 i = 0; i < bcount; i++)
fRequest->Stream().AddUInt(bitmap[i]);
_AttrBitmap(fRequest->Stream(), attrs, count);
fOpCount++;
@ -145,6 +134,32 @@ RequestBuilder::PutRootFH()
}
status_t
RequestBuilder::ReadDir(uint32 count, uint64* cookie, Attribute* attrs,
uint32 attr_count)
{
(void)count;
if (fProcedure != ProcCompound)
return B_BAD_VALUE;
if (fRequest == NULL)
return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReadDir);
fRequest->Stream().AddUHyper(cookie[0]);
fRequest->Stream().AddUHyper(cookie[1]);
// consider predicting this values basing on count or buffer size
fRequest->Stream().AddUInt(0x2000);
fRequest->Stream().AddUInt(0x8000);
_AttrBitmap(fRequest->Stream(), attrs, attr_count);
fOpCount++;
return B_OK;
}
RPC::Call*
RequestBuilder::Request()
{
@ -157,3 +172,21 @@ RequestBuilder::Request()
return NULL;
}
void
RequestBuilder::_AttrBitmap(XDR::WriteStream& stream, Attribute* attrs,
uint32 count)
{
// 2 is safe in NFS4, not in NFS4.1 though
uint32 bitmap[2];
memset(bitmap, 0, sizeof(bitmap));
for (uint32 i = 0; i < count; i++) {
bitmap[attrs[i] / 32] |= 1 << attrs[i] % 32;
}
uint32 bcount = bitmap[1] != 0 ? 2 : 1;
stream.AddUInt(bcount);
for (uint32 i = 0; i < bcount; i++)
stream.AddUInt(bitmap[i]);
}

View File

@ -27,10 +27,15 @@ public:
status_t LookUp(const char* name);
status_t PutFH(const Filehandle& fh);
status_t PutRootFH();
status_t ReadDir(uint32 count, uint64* cookie,
Attribute* attrs, uint32 attr_count);
RPC::Call* Request();
private:
void _AttrBitmap(XDR::WriteStream& stream,
Attribute* attrs, uint32 count);
uint32 fOpCount;
XDR::Stream::Position fOpCountPosition;

View File

@ -42,7 +42,8 @@ nfs4_mount(fs_volume* volume, const char* device, uint32 flags,
Filesystem* fs;
// hardcoded path
result = Filesystem::Mount(&fs, server, "haiku/src/add-ons/kernel");
result = Filesystem::Mount(&fs, server, "haiku/src/add-ons/kernel",
volume->id);
if (result != B_OK) {
gRPCServerManager->Release(server);
return result;
@ -98,7 +99,7 @@ nfs4_read_stat(fs_volume* volume, fs_vnode* vnode, struct stat* stat)
static status_t
nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
{
uint64* cookie = new(std::nothrow) uint64;
uint64* cookie = new(std::nothrow) uint64[2];
if (cookie == NULL)
return B_NO_MEMORY;
*_cookie = cookie;
@ -122,11 +123,21 @@ nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
static status_t
nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
{
delete reinterpret_cast<uint64*>(cookie);
delete[] reinterpret_cast<uint64*>(cookie);
return B_OK;
}
static status_t
nfs4_read_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie,
struct dirent* buffer, size_t bufferSize, uint32* _num)
{
uint64* cookie = reinterpret_cast<uint64*>(_cookie);
Inode* inode = reinterpret_cast<Inode*>(vnode->private_node);
return inode->ReadDir(buffer, bufferSize, _num, cookie);
}
status_t
nfs4_init()
{
@ -223,7 +234,7 @@ fs_vnode_ops gNFSv4VnodeOps = {
nfs4_open_dir,
nfs4_close_dir,
nfs4_free_dir_cookie,
NULL, // read_dir()
nfs4_read_dir,
NULL, // rewind_dir,
};