nfs4: Fix problems with reading directory entries
* Inode::ReadDir missed an entry when given buffer was too small * Each OpenDirCookie now has its own copy of directory snapshot what would prevent Inode::ReadDir from accessing freed memory when removing files and reading directory entries simultaneously * Several minor issues fixed
This commit is contained in:
@@ -25,6 +25,14 @@ NameCacheEntry::NameCacheEntry(const char* name, ino_t node)
|
||||
}
|
||||
|
||||
|
||||
NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry)
|
||||
:
|
||||
fNode(entry.fNode),
|
||||
fName(strdup(entry.fName))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NameCacheEntry::~NameCacheEntry()
|
||||
{
|
||||
free(const_cast<char*>(fName));
|
||||
@@ -37,6 +45,26 @@ DirectoryCacheSnapshot::DirectoryCacheSnapshot()
|
||||
}
|
||||
|
||||
|
||||
DirectoryCacheSnapshot::DirectoryCacheSnapshot(
|
||||
const DirectoryCacheSnapshot& snapshot)
|
||||
{
|
||||
mutex_init(&fLock, NULL);
|
||||
|
||||
MutexLocker _(snapshot.fLock);
|
||||
NameCacheEntry* entry = snapshot.fEntries.Head();
|
||||
NameCacheEntry* new_entry;
|
||||
while (entry) {
|
||||
new_entry = new NameCacheEntry(*entry);
|
||||
if (new_entry == NULL)
|
||||
break;
|
||||
|
||||
fEntries.Add(new_entry);
|
||||
|
||||
entry = snapshot.fEntries.GetNext(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DirectoryCacheSnapshot::~DirectoryCacheSnapshot()
|
||||
{
|
||||
while (!fEntries.IsEmpty()) {
|
||||
|
||||
@@ -24,15 +24,18 @@ struct NameCacheEntry :
|
||||
const char* fName;
|
||||
|
||||
NameCacheEntry(const char* name, ino_t node);
|
||||
NameCacheEntry(const NameCacheEntry& entry);
|
||||
~NameCacheEntry();
|
||||
};
|
||||
|
||||
struct DirectoryCacheSnapshot : public KernelReferenceable {
|
||||
SinglyLinkedList<NameCacheEntry> fEntries;
|
||||
mutex fLock;
|
||||
SinglyLinkedList<NameCacheEntry> fEntries;
|
||||
mutable mutex fLock;
|
||||
|
||||
DirectoryCacheSnapshot();
|
||||
~DirectoryCacheSnapshot();
|
||||
DirectoryCacheSnapshot();
|
||||
DirectoryCacheSnapshot(
|
||||
const DirectoryCacheSnapshot& snapshot);
|
||||
~DirectoryCacheSnapshot();
|
||||
};
|
||||
|
||||
class DirectoryCache : public DoublyLinkedListLinkImpl<DirectoryCache> {
|
||||
|
||||
@@ -250,7 +250,7 @@ Inode::Link(Inode* dir, const char* name)
|
||||
&& dir->fCache->ChangeInfo() == changeInfo.fBefore) {
|
||||
dir->fCache->AddEntry(name, fInfo.fFileId, true);
|
||||
dir->fCache->SetChangeInfo(changeInfo.fAfter);
|
||||
} else if (dir->fCache->ChangeInfo() != changeInfo.fBefore)
|
||||
} else
|
||||
dir->fCache->Trash();
|
||||
}
|
||||
dir->fCache->Unlock();
|
||||
@@ -428,7 +428,7 @@ Inode::CreateObject(const char* name, const char* path, int mode, FileType type,
|
||||
if (changeInfo.fAtomic && fCache->ChangeInfo() == changeInfo.fBefore) {
|
||||
fCache->AddEntry(name, fileID, true);
|
||||
fCache->SetChangeInfo(changeInfo.fAfter);
|
||||
} else if (fCache->ChangeInfo() != changeInfo.fBefore)
|
||||
} else
|
||||
fCache->Trash();
|
||||
}
|
||||
fCache->Unlock();
|
||||
|
||||
@@ -123,7 +123,7 @@ Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
|
||||
ASSERT(de != NULL);
|
||||
ASSERT(name != NULL);
|
||||
|
||||
uint32 nameSize = strlen(name);
|
||||
uint32 nameSize = strlen(name) + 1;
|
||||
const uint32 entSize = sizeof(struct dirent);
|
||||
|
||||
if (pos + entSize + nameSize > size)
|
||||
@@ -282,7 +282,7 @@ Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
|
||||
free(const_cast<char*>(name));
|
||||
|
||||
if (entry == NULL || entry->fName == NULL) {
|
||||
if (entry->fName == NULL)
|
||||
if (entry != NULL)
|
||||
delete entry;
|
||||
delete snapshot;
|
||||
delete[] dirents;
|
||||
@@ -324,10 +324,10 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
||||
else
|
||||
fFileSystem->Revalidator().RemoveDirectory(cache);
|
||||
|
||||
cookie->fSnapshot = cache->GetSnapshot();
|
||||
if (cookie->fSnapshot == NULL) {
|
||||
DirectoryCacheSnapshot* snapshot = cache->GetSnapshot();
|
||||
if (snapshot == NULL) {
|
||||
uint64 change;
|
||||
result = GetDirSnapshot(&cookie->fSnapshot, cookie, &change,
|
||||
result = GetDirSnapshot(&snapshot, cookie, &change,
|
||||
cookie->fAttrDir);
|
||||
if (result != B_OK) {
|
||||
cache->Unlock();
|
||||
@@ -335,9 +335,9 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
||||
return result;
|
||||
}
|
||||
cache->ValidateChangeInfo(change);
|
||||
cache->SetSnapshot(cookie->fSnapshot);
|
||||
cache->SetSnapshot(snapshot);
|
||||
}
|
||||
cookie->fSnapshot->AcquireReference();
|
||||
cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot);
|
||||
fFileSystem->Revalidator().AddDirectory(cache);
|
||||
cache->Unlock();
|
||||
fFileSystem->Revalidator().Unlock();
|
||||
@@ -387,6 +387,7 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
||||
MutexLocker _(cookie->fSnapshot->fLock);
|
||||
for (; !overflow && i < *_count; i++) {
|
||||
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
|
||||
NameCacheEntry* temp = cookie->fCurrent;
|
||||
|
||||
if (cookie->fCurrent == NULL)
|
||||
cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
|
||||
@@ -402,6 +403,7 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
|
||||
|
||||
if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
|
||||
pos, size) == B_BUFFER_OVERFLOW) {
|
||||
cookie->fCurrent = temp;
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ Inode::CreateState(const char* name, int mode, int perms, OpenState* state,
|
||||
&& fCache->ChangeInfo() == changeInfo.fBefore) {
|
||||
fCache->AddEntry(name, fileID, true);
|
||||
fCache->SetChangeInfo(changeInfo.fAfter);
|
||||
} else if (fCache->ChangeInfo() != changeInfo.fBefore)
|
||||
} else
|
||||
fCache->Trash();
|
||||
}
|
||||
fCache->Unlock();
|
||||
|
||||
@@ -421,10 +421,7 @@ ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
|
||||
|
||||
isNext = fReply->Stream().GetBoolean();
|
||||
}
|
||||
if (!isNext)
|
||||
*eof = fReply->Stream().GetBoolean();
|
||||
else
|
||||
*eof = false;
|
||||
*eof = fReply->Stream().GetBoolean();
|
||||
|
||||
*_count = count;
|
||||
*dirents = entries;
|
||||
|
||||
@@ -1039,6 +1039,9 @@ nfs4_rewind_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
|
||||
|
||||
OpenDirCookie* cookie = reinterpret_cast<OpenDirCookie*>(_cookie);
|
||||
cookie->fSpecial = 0;
|
||||
if (cookie->fSnapshot != NULL)
|
||||
cookie->fSnapshot->ReleaseReference();
|
||||
cookie->fSnapshot = NULL;
|
||||
cookie->fCurrent = NULL;
|
||||
cookie->fEOF = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user