PR#259071 provides a test program that fails for the NFS client.
Testing with it, there appears to be a race between Lookup
and VOPs like Setattr-of-size, where Lookup ends up loading
stale attributes (including what might be the wrong file size)
into the NFS vnode's attribute cache.
The race occurs when the modifying VOP (which holds a lock
on the vnode), blocks the acquisition of the vnode in Lookup,
after the RPC (with now potentially stale attributes).
Here's what seems to happen:
Child Parent
- does stat()
- does VOP_LOOKUP(), which does the Lookup RPC with the directory vnode locked - does ftruncate(), acquiring --> acquires file handle and exclusive vnode lock on file vnode attributes, including Size, valid at this point in time
- blocks waiting for locked file vnode - does VOP_SETATTR() of Size, changing the file's size
- releases the file vnode
- acquires file vnode and fills in now stale attributes including the old wrong Size
- does a read() which returns wrong data size
This patch fixes the problem by saving a timestamp in the NFS vnode
in the VOPs that modify the file (Setattr-of-size, Allocate, Deallocate).
Then lookup compares that timestamp with the time just before
starting the Lookup RPC after it has acquired the file's vnode.
If the modifying RPC occurred during the Lookup, the attributes
in the RPC reply are discarded, since they might be stale.
Although Deallocate never changes file size, it does change
modify_time and the NFSv4 Change attribute, so I included it.
With this patch the test program works as expected.
I believe there is a similar race between Lookup and Writes,
which I am working on a separate patch for,