mount: check the creation time when the base has no inode

An entry without an inode or hard link id matched any replacement at the
same path, so a stale handle could merge into a different file. Without
an inode the merge now needs the same creation time. The filer keeps it
across updates; only a synthesized base has neither and still merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chris LuandClaude Opus 5.5 committed 2026-11-12 17:35:58 -08:00
1 parent f44eeff31d
commit 2d719fb4ea
2 files changed
+20 -3

No files matched your search

@@ -354,6 +354,18 @@ func TestMergeContentEntry(t *testing.T) {
t.Fatal("refused a truncate that shortened an unstamped base chunk")
}
noInode := proto.Clone(base).(*filer_pb.Entry)
noInode.Attributes.Inode, noInode.Attributes.Crtime = 0, 5
sameFile := proto.Clone(noInode).(*filer_pb.Entry)
sameFile.Chunks = append(sameFile.Chunks, stored.Chunks[2])
if _, ok := mergeContentEntry(sameFile, noInode, local, local, -1); !ok {
t.Fatal("refused an inode-less file with the same creation time")
}
sameFile.Attributes.Crtime = 6
if _, ok := mergeContentEntry(sameFile, noInode, local, local, -1); ok {
t.Fatal("merged into an inode-less replacement")
}
empty := flushConditionEntry()
inline := proto.Clone(empty).(*filer_pb.Entry)
inline.Content = []byte("peer")
+8 -3
View File
@@ -485,13 +485,18 @@ func mergeContentEntry(stored, base, local, sent *filer_pb.Entry, truncatedTo in
return merged, true
}
// sameContentFile reports whether stored is still the regular file base was.
// sameContentFile reports whether stored is still the regular file base was:
// the same inode, or for an entry without one, the same creation time. A
// synthesized base with neither cannot tell.
func sameContentFile(stored, base *filer_pb.Entry) bool {
if stored == nil || base == nil || stored.IsDirectory || !bytes.Equal(stored.HardLinkId, base.HardLinkId) {
return false
}
inode := base.GetAttributes().GetInode()
return inode == 0 || stored.GetAttributes().GetInode() == inode
if inode := base.GetAttributes().GetInode(); inode != 0 {
return stored.GetAttributes().GetInode() == inode
}
crtime := base.GetAttributes().GetCrtime()
return crtime == 0 || stored.GetAttributes().GetCrtime() == crtime
}
// contentChunkKey identifies a chunk reference: truncation shrinks a chunk in