diff --git a/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp b/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp index 282a33d3e8..b7149b08fc 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Filesystem.cpp @@ -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); } diff --git a/src/add-ons/kernel/file_systems/nfs4/Filesystem.h b/src/add-ons/kernel/file_systems/nfs4/Filesystem.h index ca370c28de..a9386de8d2 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Filesystem.h +++ b/src/add-ons/kernel/file_systems/nfs4/Filesystem.h @@ -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 diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp index 630e1fdad6..329730f634 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp @@ -9,6 +9,7 @@ #include "Inode.h" +#include #include #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(_buffer); + + if (cookie[0] == 0 && cookie[1] == 2 && count < *_count) { + struct dirent* de = reinterpret_cast(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(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(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; } diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.h b/src/add-ons/kernel/file_systems/nfs4/Inode.h index e9ed6ca588..b7a8528766 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.h +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.h @@ -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); } diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h index f9f4622fde..3ea863096f 100644 --- a/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h @@ -32,7 +32,8 @@ enum Opcode { OpGetFH = 10, OpLookUp = 15, OpPutFH = 22, - OpPutRootFH = 24 + OpPutRootFH = 24, + OpReadDir = 26 }; enum Access { diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp index 1cb5158609..b0137b01f9 100644 --- a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp @@ -14,6 +14,22 @@ #include +DirEntry::DirEntry() + : + fName(NULL), + fAttrs(NULL), + fAttrCount(0) +{ +} + + +DirEntry::~DirEntry() +{ + free(const_cast(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(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) { diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h index 4361095bc7..8eebf24b9e 100644 --- a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h @@ -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); diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp index 2ed5b59df5..2e9bfe7f04 100644 --- a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp @@ -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]); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h index 7f8b3b3ba1..838d2ae82c 100644 --- a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h +++ b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h @@ -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; diff --git a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp index df9be9f1e3..f9423044a1 100644 --- a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp @@ -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(cookie); + delete[] reinterpret_cast(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(_cookie); + Inode* inode = reinterpret_cast(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, };