nfs4: Check for stale nodes when reload dir cache
* When the DirectoryCache is updated, compare the new contents with the old. If any of the old entries are obsolete, treat the respective node as stale. * Create FileSystem::TrashStaleNode() to avoid duplicating code. * Possible fix for #19656. This helps the client keep current with changes made to server files by other users. While the comparison will be slow for a large directory, it won't be executed unless another user has changed the contents of a directory since the last time the Haiku client read the directory. Change-Id: I03e65aaaad5027c66b10265f2b4a21ed9d2b6744 Reviewed-on: https://review.haiku-os.org/c/haiku/+/9445 Tested-by: Commit checker robot <[email protected]> Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
@@ -206,9 +206,6 @@ DirectoryCache::_LoadSnapshot(bool trash)
|
||||
if (oldSnapshot != NULL)
|
||||
oldSnapshot->AcquireReference();
|
||||
|
||||
if (trash)
|
||||
Trash();
|
||||
|
||||
DirectoryCacheSnapshot* newSnapshot;
|
||||
status_t result = fInode->GetDirSnapshot(&newSnapshot, NULL, &fChange,
|
||||
fAttrDir);
|
||||
@@ -222,6 +219,31 @@ DirectoryCache::_LoadSnapshot(bool trash)
|
||||
_SetSnapshot(newSnapshot);
|
||||
fExpireTime = system_time() + fExpirationTime;
|
||||
|
||||
if (trash) {
|
||||
// Clear fNameCache, while checking for mismatches between its entries and the newly
|
||||
// obtained snapshot that might indicate stale nodes.
|
||||
while (!fNameCache.IsEmpty()) {
|
||||
NameCacheEntry* current = fNameCache.RemoveHead();
|
||||
bool nodeFound = false;
|
||||
for (SinglyLinkedList<NameCacheEntry>::ConstIterator it
|
||||
= newSnapshot->fEntries.GetIterator();
|
||||
NameCacheEntry* snapshotEntry = it.Next();) {
|
||||
if (current->fNode == snapshotEntry->fNode
|
||||
&& strcmp(current->fName, snapshotEntry->fName) == 0) {
|
||||
nodeFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!nodeFound) {
|
||||
// The inode-name association that was cached in 'current' is no longer valid.
|
||||
result = fInode->GetFileSystem()->TrashStaleNode(current->fNode);
|
||||
if (result != B_OK)
|
||||
INFORM("_LoadSnapshot: Couldn't free stale node.\n");
|
||||
}
|
||||
delete current;
|
||||
}
|
||||
}
|
||||
|
||||
fTrashed = false;
|
||||
|
||||
if (oldSnapshot != NULL)
|
||||
|
||||
@@ -469,6 +469,41 @@ FileSystem::GetDelegation(const FileHandle& handle)
|
||||
return it.Current();
|
||||
}
|
||||
|
||||
/*! If a node object with this inode number exists, free it at the FS and VFS levels to
|
||||
ensure that no operation will try to use it.
|
||||
|
||||
*/
|
||||
status_t
|
||||
FileSystem::TrashStaleNode(ino_t ino)
|
||||
{
|
||||
status_t result = B_OK;
|
||||
if (acquire_vnode(fFsVolume, ino) == B_OK) {
|
||||
// we still have a vnode for the stale file present in memory
|
||||
|
||||
// mark it as stale
|
||||
VnodeToInode* vti;
|
||||
result = get_vnode(fFsVolume, ino, reinterpret_cast<void**>(&vti));
|
||||
ASSERT(result == B_OK);
|
||||
Inode* inode = vti->GetPointer();
|
||||
if (inode != NULL)
|
||||
inode->SetStale();
|
||||
put_vnode(fFsVolume, ino);
|
||||
|
||||
// delete it
|
||||
result = remove_vnode(fFsVolume, ino);
|
||||
ASSERT(result == B_OK);
|
||||
put_vnode(fFsVolume, ino);
|
||||
|
||||
// verify it is gone
|
||||
if (acquire_vnode(fFsVolume, ino) == B_OK)
|
||||
result = B_ERROR;
|
||||
else
|
||||
result = B_OK;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*! Used when creating a file to check for a stale node with the same ino. If it exists,
|
||||
the stale node is deleted.
|
||||
@@ -488,28 +523,8 @@ FileSystem::EnsureNoCollision(ino_t newID, const FileHandle& handle)
|
||||
// We are already using this file ID for a previously existing file. If the server has
|
||||
// assigned that ID to the file that we are now creating, it means someone else must have
|
||||
// deleted the other file from the server, and the server is recycling the file ID.
|
||||
result = acquire_vnode(fFsVolume, newID);
|
||||
if (result == B_OK) {
|
||||
// we still have a vnode for the stale file present in memory
|
||||
|
||||
// mark it as stale
|
||||
VnodeToInode* vti;
|
||||
result = get_vnode(fFsVolume, newID, reinterpret_cast<void**>(&vti));
|
||||
ASSERT(result == B_OK);
|
||||
Inode* inode = vti->GetPointer();
|
||||
if (inode != NULL)
|
||||
inode->SetStale();
|
||||
put_vnode(fFsVolume, newID);
|
||||
|
||||
// delete it
|
||||
result = remove_vnode(fFsVolume, newID);
|
||||
ASSERT(result == B_OK);
|
||||
put_vnode(fFsVolume, newID);
|
||||
|
||||
// verify it is gone
|
||||
result = acquire_vnode(fFsVolume, newID);
|
||||
ASSERT(result != B_OK);
|
||||
}
|
||||
result = TrashStaleNode(newID);
|
||||
ASSERT(result == B_OK);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
|
||||
inline mutex& CreateFileLock();
|
||||
|
||||
status_t TrashStaleNode(ino_t ino);
|
||||
void EnsureNoCollision(ino_t newID, const FileHandle& handle);
|
||||
|
||||
void Dump(void (*xprintf)(const char*, ...) = dprintf);
|
||||
|
||||
Reference in New Issue
Block a user