nfs4: Add full directory cache implementation
This commit is contained in:
@@ -85,7 +85,8 @@ CacheRevalidator::_DirectoryCacheRevalidator()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
current->Lock();
|
if (current->Lock() != B_OK)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (current->ExpireTime() > system_time()) {
|
if (current->ExpireTime() > system_time()) {
|
||||||
current->Unlock();
|
current->Unlock();
|
||||||
|
|||||||
@@ -233,3 +233,10 @@ OpenFileCookie::_ReleaseLockOwner(LockOwner* owner)
|
|||||||
return reply.ReleaseLockOwner();
|
return reply.ReleaseLockOwner();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OpenDirCookie::~OpenDirCookie()
|
||||||
|
{
|
||||||
|
if (fSnapshot != NULL)
|
||||||
|
fSnapshot->ReleaseReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,8 +106,11 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct OpenDirCookie : public Cookie {
|
struct OpenDirCookie : public Cookie {
|
||||||
uint64 fCookie;
|
DirectoryCacheSnapshot* fSnapshot;
|
||||||
uint64 fCookieVerf;
|
NameCacheEntry* fCurrent;
|
||||||
|
bool fEOF;
|
||||||
|
|
||||||
|
~OpenDirCookie();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,41 @@
|
|||||||
#include "Inode.h"
|
#include "Inode.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NameCacheEntry::NameCacheEntry(const char* name, ino_t node)
|
||||||
|
:
|
||||||
|
fNode(node),
|
||||||
|
fName(strdup(name))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NameCacheEntry::~NameCacheEntry()
|
||||||
|
{
|
||||||
|
free(const_cast<char*>(fName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryCacheSnapshot::DirectoryCacheSnapshot()
|
||||||
|
{
|
||||||
|
mutex_init(&fLock, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DirectoryCacheSnapshot::~DirectoryCacheSnapshot()
|
||||||
|
{
|
||||||
|
while (!fEntries.IsEmpty()) {
|
||||||
|
NameCacheEntry* current = fEntries.RemoveHead();
|
||||||
|
delete current;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_destroy(&fLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DirectoryCache::DirectoryCache(Inode* inode)
|
DirectoryCache::DirectoryCache(Inode* inode)
|
||||||
:
|
:
|
||||||
|
fDirectoryCache(NULL),
|
||||||
fInode(inode),
|
fInode(inode),
|
||||||
fTrashed(true)
|
fTrashed(true)
|
||||||
{
|
{
|
||||||
@@ -46,27 +79,26 @@ DirectoryCache::Trash()
|
|||||||
NameCacheEntry* current = fNameCache.RemoveHead();
|
NameCacheEntry* current = fNameCache.RemoveHead();
|
||||||
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
|
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
|
||||||
current->fName);
|
current->fName);
|
||||||
free(const_cast<char*>(current->fName));
|
|
||||||
delete current;
|
delete current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetSnapshot(NULL);
|
||||||
|
|
||||||
fTrashed = true;
|
fTrashed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: separate AddEntry() for Name and Directory Cache are needed
|
||||||
status_t
|
status_t
|
||||||
DirectoryCache::AddEntry(const char* name, ino_t node)
|
DirectoryCache::AddEntry(const char* name, ino_t node)
|
||||||
{
|
{
|
||||||
NameCacheEntry* entry = new(std::nothrow) NameCacheEntry;
|
NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node);
|
||||||
if (entry == NULL)
|
if (entry == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
entry->fName = strdup(name);
|
|
||||||
if (entry->fName == NULL) {
|
if (entry->fName == NULL) {
|
||||||
delete entry;
|
delete entry;
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
}
|
}
|
||||||
entry->fNode = node;
|
|
||||||
fNameCache.Add(entry);
|
fNameCache.Add(entry);
|
||||||
|
|
||||||
return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(), name,
|
return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(), name,
|
||||||
@@ -82,10 +114,8 @@ DirectoryCache::RemoveEntry(const char* name)
|
|||||||
NameCacheEntry* current = iterator.Next();
|
NameCacheEntry* current = iterator.Next();
|
||||||
while (current != NULL) {
|
while (current != NULL) {
|
||||||
if (strcmp(current->fName, name) == 0) {
|
if (strcmp(current->fName, name) == 0) {
|
||||||
free(const_cast<char*>(current->fName));
|
|
||||||
delete current;
|
|
||||||
|
|
||||||
fNameCache.Remove(previous, current);
|
fNameCache.Remove(previous, current);
|
||||||
|
delete current;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +123,36 @@ DirectoryCache::RemoveEntry(const char* name)
|
|||||||
current = iterator.Next();
|
current = iterator.Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fDirectoryCache != NULL) {
|
||||||
|
MutexLocker _(fDirectoryCache->fLock);
|
||||||
|
iterator = fDirectoryCache->fEntries.GetIterator();
|
||||||
|
previous = NULL;
|
||||||
|
current = iterator.Next();
|
||||||
|
while (current != NULL) {
|
||||||
|
if (strcmp(current->fName, name) == 0) {
|
||||||
|
fDirectoryCache->fEntries.Remove(previous, current);
|
||||||
|
delete current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = current;
|
||||||
|
current = iterator.Next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), name);
|
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
DirectoryCache::SetSnapshot(DirectoryCacheSnapshot* snapshot)
|
||||||
|
{
|
||||||
|
if (fDirectoryCache != NULL)
|
||||||
|
fDirectoryCache->ReleaseReference();
|
||||||
|
fDirectoryCache = snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
DirectoryCache::Revalidate()
|
DirectoryCache::Revalidate()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
#include <SupportDefs.h>
|
#include <SupportDefs.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
|
#include <util/KernelReferenceable.h>
|
||||||
#include <util/SinglyLinkedList.h>
|
#include <util/SinglyLinkedList.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -21,8 +22,18 @@ struct NameCacheEntry :
|
|||||||
public SinglyLinkedListLinkImpl<NameCacheEntry> {
|
public SinglyLinkedListLinkImpl<NameCacheEntry> {
|
||||||
ino_t fNode;
|
ino_t fNode;
|
||||||
const char* fName;
|
const char* fName;
|
||||||
|
|
||||||
|
NameCacheEntry(const char* name, ino_t node);
|
||||||
|
~NameCacheEntry();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DirectoryCacheSnapshot : public KernelReferenceable {
|
||||||
|
SinglyLinkedList<NameCacheEntry> fEntries;
|
||||||
|
mutex fLock;
|
||||||
|
|
||||||
|
DirectoryCacheSnapshot();
|
||||||
|
~DirectoryCacheSnapshot();
|
||||||
|
};
|
||||||
|
|
||||||
class DirectoryCache : public DoublyLinkedListLinkImpl<DirectoryCache> {
|
class DirectoryCache : public DoublyLinkedListLinkImpl<DirectoryCache> {
|
||||||
public:
|
public:
|
||||||
@@ -38,6 +49,9 @@ public:
|
|||||||
status_t AddEntry(const char* name, ino_t node);
|
status_t AddEntry(const char* name, ino_t node);
|
||||||
void RemoveEntry(const char* name);
|
void RemoveEntry(const char* name);
|
||||||
|
|
||||||
|
void SetSnapshot(DirectoryCacheSnapshot* snapshot);
|
||||||
|
inline DirectoryCacheSnapshot* GetSnapshot();
|
||||||
|
|
||||||
inline SinglyLinkedList<NameCacheEntry>& EntriesList();
|
inline SinglyLinkedList<NameCacheEntry>& EntriesList();
|
||||||
|
|
||||||
status_t Revalidate();
|
status_t Revalidate();
|
||||||
@@ -53,6 +67,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
SinglyLinkedList<NameCacheEntry> fNameCache;
|
SinglyLinkedList<NameCacheEntry> fNameCache;
|
||||||
|
|
||||||
|
DirectoryCacheSnapshot* fDirectoryCache;
|
||||||
|
//mutex fDirectoryCacheLock;
|
||||||
|
|
||||||
Inode* fInode;
|
Inode* fInode;
|
||||||
|
|
||||||
bool fTrashed;
|
bool fTrashed;
|
||||||
@@ -82,7 +99,14 @@ DirectoryCache::Unlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline SinglyLinkedList<NameCacheEntry>&
|
inline DirectoryCacheSnapshot*
|
||||||
|
DirectoryCache::GetSnapshot()
|
||||||
|
{
|
||||||
|
return fDirectoryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline SinglyLinkedList<NameCacheEntry>&
|
||||||
DirectoryCache::EntriesList()
|
DirectoryCache::EntriesList()
|
||||||
{
|
{
|
||||||
return fNameCache;
|
return fNameCache;
|
||||||
@@ -94,8 +118,10 @@ DirectoryCache::ValidateChangeInfo(uint64 change)
|
|||||||
{
|
{
|
||||||
if (fTrashed || change != fChange) {
|
if (fTrashed || change != fChange) {
|
||||||
Trash();
|
Trash();
|
||||||
change = fChange;
|
fChange = change;
|
||||||
fExpireTime = system_time() + kExpirationTime;
|
fExpireTime = system_time() + kExpirationTime;
|
||||||
|
fTrashed = false;
|
||||||
|
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,11 +81,16 @@ protected:
|
|||||||
OpenFileCookie* cookie);
|
OpenFileCookie* cookie);
|
||||||
|
|
||||||
status_t _ReadDirOnce(DirEntry** dirents, uint32* count,
|
status_t _ReadDirOnce(DirEntry** dirents, uint32* count,
|
||||||
OpenDirCookie* cookie, bool* eof);
|
OpenDirCookie* cookie, bool* eof,
|
||||||
|
uint64* change, uint64* dirCookie,
|
||||||
|
uint64* dirCookieVerf);
|
||||||
status_t _FillDirEntry(struct dirent* de, ino_t id,
|
status_t _FillDirEntry(struct dirent* de, ino_t id,
|
||||||
const char* name, uint32 pos, uint32 size);
|
const char* name, uint32 pos, uint32 size);
|
||||||
status_t _ReadDirUp(struct dirent* de, uint32 pos,
|
status_t _ReadDirUp(struct dirent* de, uint32 pos,
|
||||||
uint32 size);
|
uint32 size);
|
||||||
|
status_t _GetDirSnapshot(DirectoryCacheSnapshot**
|
||||||
|
_snapshot, OpenDirCookie* cookie,
|
||||||
|
uint64* _change);
|
||||||
|
|
||||||
static inline status_t _CheckLockType(short ltype, uint32 mode);
|
static inline status_t _CheckLockType(short ltype, uint32 mode);
|
||||||
|
|
||||||
|
|||||||
@@ -122,8 +122,9 @@ Inode::OpenDir(OpenDirCookie* cookie)
|
|||||||
return B_PERMISSION_DENIED;
|
return B_PERMISSION_DENIED;
|
||||||
|
|
||||||
cookie->fFileSystem = fFileSystem;
|
cookie->fFileSystem = fFileSystem;
|
||||||
cookie->fCookie = 0;
|
cookie->fSnapshot = NULL;
|
||||||
cookie->fCookieVerf = 2;
|
cookie->fCurrent = NULL;
|
||||||
|
cookie->fEOF = false;
|
||||||
|
|
||||||
fFileSystem->Root()->MakeInfoInvalid();
|
fFileSystem->Root()->MakeInfoInvalid();
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ Inode::OpenDir(OpenDirCookie* cookie)
|
|||||||
|
|
||||||
status_t
|
status_t
|
||||||
Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
|
Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
|
||||||
bool* eof)
|
bool* eof, uint64* change, uint64* dirCookie, uint64* dirCookieVerf)
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
RPC::Server* serv = fFileSystem->Server();
|
RPC::Server* serv = fFileSystem->Server();
|
||||||
@@ -143,10 +144,16 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
|
|||||||
|
|
||||||
req.PutFH(fInfo.fHandle);
|
req.PutFH(fInfo.fHandle);
|
||||||
|
|
||||||
|
Attribute dirAttr[] = { FATTR4_CHANGE };
|
||||||
|
if (*change == 0)
|
||||||
|
req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute));
|
||||||
|
|
||||||
Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
|
Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
|
||||||
req.ReadDir(*count, cookie->fCookie, cookie->fCookieVerf, attr,
|
req.ReadDir(*count, *dirCookie, *dirCookieVerf, attr,
|
||||||
sizeof(attr) / sizeof(Attribute));
|
sizeof(attr) / sizeof(Attribute));
|
||||||
|
|
||||||
|
req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute));
|
||||||
|
|
||||||
status_t result = request.Send(cookie);
|
status_t result = request.Send(cookie);
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
@@ -157,8 +164,39 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
reply.PutFH();
|
reply.PutFH();
|
||||||
return reply.ReadDir(&cookie->fCookie, &cookie->fCookieVerf, dirents,
|
|
||||||
|
AttrValue* before = NULL;
|
||||||
|
uint32 attrCount;
|
||||||
|
if (*change == 0) {
|
||||||
|
result = reply.GetAttr(&before, &attrCount);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = reply.ReadDir(dirCookie, dirCookieVerf, dirents,
|
||||||
count, eof);
|
count, eof);
|
||||||
|
if (result != B_OK) {
|
||||||
|
delete[] before;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttrValue* after;
|
||||||
|
result = reply.GetAttr(&after, &attrCount);
|
||||||
|
if (result != B_OK) {
|
||||||
|
delete[] before;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*change == 0 && before[0].fData.fValue64 == after[0].fData.fValue64
|
||||||
|
|| *change == after[0].fData.fValue64)
|
||||||
|
*change = after[0].fData.fValue64;
|
||||||
|
else
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
delete[] before;
|
||||||
|
delete[] after;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,23 +274,67 @@ Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
|
|||||||
} while (true);
|
} 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
|
status_t
|
||||||
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
Inode::_GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
|
||||||
OpenDirCookie* cookie)
|
OpenDirCookie* cookie, uint64* _change)
|
||||||
{
|
{
|
||||||
uint32 count = 0;
|
DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot;
|
||||||
uint32 pos = 0;
|
if (snapshot == NULL)
|
||||||
uint32 this_count;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
uint64 change = 0;
|
||||||
|
uint64 dirCookie = 0;
|
||||||
|
uint64 dirCookieVerf = 0;
|
||||||
bool eof = false;
|
bool eof = false;
|
||||||
|
|
||||||
char* buffer = reinterpret_cast<char*>(_buffer);
|
while (!eof) {
|
||||||
|
uint32 count;
|
||||||
|
DirEntry* dirents;
|
||||||
|
|
||||||
|
status_t result = _ReadDirOnce(&dirents, &count, cookie, &eof, &change,
|
||||||
|
&dirCookie, &dirCookieVerf);
|
||||||
|
if (result != B_OK) {
|
||||||
|
delete snapshot;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 i;
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
NameCacheEntry* entry = new NameCacheEntry(dirents[i].fName, id);
|
||||||
|
if (entry == NULL || entry->fName == NULL) {
|
||||||
|
if (entry->fName == NULL)
|
||||||
|
delete entry;
|
||||||
|
delete snapshot;
|
||||||
|
delete[] dirents;
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
snapshot->fEntries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] dirents;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_snapshot = snapshot;
|
||||||
|
*_change = change;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if (cookie->fCookie == 0 && cookie->fCookieVerf == 2 && count < *_count) {
|
if (cookie->fCookie == 0 && cookie->fCookieVerf == 2 && count < *_count) {
|
||||||
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
|
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
|
||||||
|
|
||||||
@@ -275,50 +357,78 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
|||||||
count++;
|
count++;
|
||||||
cookie->fCookieVerf--;
|
cookie->fCookieVerf--;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
bool overflow = false;
|
status_t
|
||||||
while (count < *_count && !eof) {
|
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
||||||
this_count = *_count - count;
|
OpenDirCookie* cookie)
|
||||||
DirEntry* dirents;
|
{
|
||||||
|
if (cookie->fEOF) {
|
||||||
status_t result = _ReadDirOnce(&dirents, &this_count, cookie, &eof);
|
*_count = 0;
|
||||||
if (result != B_OK)
|
return 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)
|
status_t result;
|
||||||
|
if (cookie->fSnapshot == NULL) {
|
||||||
|
fFileSystem->Revalidator().Lock();
|
||||||
|
if (fCache->Lock() != B_OK) {
|
||||||
|
fCache->ResetAndLock();
|
||||||
|
} else {
|
||||||
|
fFileSystem->Revalidator().RemoveDirectory(fCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie->fSnapshot = fCache->GetSnapshot();
|
||||||
|
if (cookie->fSnapshot == NULL) {
|
||||||
|
uint64 change;
|
||||||
|
result = _GetDirSnapshot(&cookie->fSnapshot, cookie, &change);
|
||||||
|
if (result != B_OK) {
|
||||||
|
fCache->Unlock();
|
||||||
|
fFileSystem->Revalidator().Unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
fCache->ValidateChangeInfo(change);
|
||||||
|
fCache->SetSnapshot(cookie->fSnapshot);
|
||||||
|
}
|
||||||
|
cookie->fSnapshot->AcquireReference();
|
||||||
|
fFileSystem->Revalidator().AddDirectory(fCache);
|
||||||
|
fCache->Unlock();
|
||||||
|
fFileSystem->Revalidator().Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
char* buffer = reinterpret_cast<char*>(_buffer);
|
||||||
|
uint32 pos = 0;
|
||||||
|
|
||||||
|
MutexLocker _(cookie->fSnapshot->fLock);
|
||||||
|
uint32 i;
|
||||||
|
bool overflow = false;
|
||||||
|
for (i = 0; i < *_count; i++) {
|
||||||
|
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
|
||||||
|
|
||||||
|
if (cookie->fCurrent == NULL)
|
||||||
|
cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
|
||||||
|
else {
|
||||||
|
cookie->fCurrent
|
||||||
|
= cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cookie->fCurrent == NULL) {
|
||||||
|
cookie->fEOF = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
|
||||||
|
pos, size) == B_BUFFER_OVERFLOW) {
|
||||||
|
overflow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += de->d_reclen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 && overflow)
|
||||||
return B_BUFFER_OVERFLOW;
|
return B_BUFFER_OVERFLOW;
|
||||||
|
|
||||||
*_count = count;
|
*_count = i;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -326,12 +326,33 @@ ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
|
|||||||
|
|
||||||
bool isNext;
|
bool isNext;
|
||||||
uint32 count = 0;
|
uint32 count = 0;
|
||||||
DirEntry* entries = new(std::nothrow) DirEntry[*_count];
|
|
||||||
|
// TODO: using list instead of array would make this much more elegant
|
||||||
|
// and efficient
|
||||||
|
XDR::Stream::Position dataStart = fReply->Stream().Current();
|
||||||
|
isNext = fReply->Stream().GetBoolean();
|
||||||
|
while (isNext) {
|
||||||
|
fReply->Stream().GetUHyper();
|
||||||
|
|
||||||
|
free(fReply->Stream().GetString());
|
||||||
|
AttrValue* values;
|
||||||
|
uint32 attrCount;
|
||||||
|
_DecodeAttrs(fReply->Stream(), &values, &attrCount);
|
||||||
|
delete[] values;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
|
||||||
|
isNext = fReply->Stream().GetBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry* entries = new(std::nothrow) DirEntry[count];
|
||||||
if (entries == NULL)
|
if (entries == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
fReply->Stream().SetPosition(dataStart);
|
||||||
isNext = fReply->Stream().GetBoolean();
|
isNext = fReply->Stream().GetBoolean();
|
||||||
while (isNext && count < *_count) {
|
while (isNext) {
|
||||||
*cookie = fReply->Stream().GetUHyper();
|
*cookie = fReply->Stream().GetUHyper();
|
||||||
|
|
||||||
entries[count].fName = fReply->Stream().GetString();
|
entries[count].fName = fReply->Stream().GetString();
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public:
|
|||||||
ReadStream(void* buffer, uint32 size);
|
ReadStream(void* buffer, uint32 size);
|
||||||
virtual ~ReadStream();
|
virtual ~ReadStream();
|
||||||
|
|
||||||
|
inline void SetPosition(Position position);
|
||||||
|
|
||||||
inline int Size() const;
|
inline int Size() const;
|
||||||
|
|
||||||
int32 GetInt();
|
int32 GetInt();
|
||||||
@@ -108,6 +110,13 @@ Stream::Current() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void
|
||||||
|
ReadStream::SetPosition(Position position)
|
||||||
|
{
|
||||||
|
fPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline int
|
inline int
|
||||||
ReadStream::Size() const
|
ReadStream::Size() const
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user