Keep the seek offset out of memfs WriteAt - #239
Open
youdie006 wants to merge 2 commits into
Open
Conversation
io.WriterAt requires WriteAt not to affect the seek offset, but memfs set position to off+n, so a caller that patched an earlier region in place then kept writing overwrote its own data. osfs already behaves correctly; Write now advances the offset itself.
There was a problem hiding this comment.
🟡 Changes recommended
The new test leaks an opened file handle (and should follow the file’s established t.Helper() convention), which should be corrected before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Fixes memfs WriteAt to honor the io.WriterAt contract (must not affect the underlying seek offset), preventing callers that interleave WriteAt and Write from accidentally overwriting data. Adds a conformance test that runs across both osfs and memfs via the shared eachBasicFS suite.
Changes:
- Update
memfs/file.gosoWriteAtno longer mutates the handle seek position; advance is performed inWriteinstead. - Add a new shared conformance test asserting
WriteAtdoes not move the seek offset and does not corrupt subsequent writes.
File summaries
| File | Description |
|---|---|
| memfs/file.go | Stops WriteAt from changing file.position; moves offset advancement into Write. |
| test/basic_test.go | Adds a cross-FS regression test validating WriteAt does not move the seek offset or corrupt data. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+838
to
+842
| read, err := fs.Open("foo") | ||
| require.NoError(t, err) | ||
| all, err := io.ReadAll(read) | ||
| require.NoError(t, err) | ||
| require.Equal(t, "HDR!0020BODYBODYBODY<EOF>", string(all)) |
Comment on lines
+818
to
+820
| eachBasicFS(t, func(t *testing.T, fs Basic) { | ||
| f, err := fs.Create("foo") | ||
| require.NoError(t, err) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
memfs'sWriteAtmoves the handle's seek offset, so a caller that patches an earlier region inplace and keeps writing overwrites its own data — with no error.
io.WriterAt, whichbilly.Fileembeds atfs.go:178:Same caller code through three implementations — write a placeholder header, write a body, patch the
header via
WriteAt, then write a footer:Every call returned a nil error. memfs rewound to 8 and wrote the footer over the body.
memfs/file.go:87:The assignment cannot simply be dropped, because
Write(memfs/file.go:70) isreturn f.WriteAt(p, f.position)and relies on that side effect to advance. So the fix moves theadvance into
Write, where it belongs, and leavesWriteAtoffset-free.osfs(a realos.File) andosfs/mmap_file.goalready honour the contract, and theFileinterface already carries a note requiring
ReadAtto matchio.ReaderAt—WriteAtis the onethat drifted.
Test
Added to the shared conformance suite in
test/basic_test.go, whicheachBasicFSruns over bothosfsandmemfs(test/common_posix.go:20), soosfssupplies the expected values rather thanme asserting them. Red with only
memfs/file.goreverted — and only the memfs subtest fails:Green with the change;
go test ./...passes across all packages andgofmt -llists neither file.grep WriteAt --include='*_test.go'finds a single hit, inosfs/mmap_file_test.go— memfs'sWriteAthad no test, and nothing anywhere interleaved it withWrite, which is the combinationthat fails.
Disclosure: prepared with AI assistance; I verified the three-way comparison and the red/green runs
myself.