Skip to content

fix(files): remove io.Seeker from File interface#1128

Merged
lidel merged 8 commits into
mainfrom
fix/files-remove-seeker-from-file-interface
May 15, 2026
Merged

fix(files): remove io.Seeker from File interface#1128
lidel merged 8 commits into
mainfrom
fix/files-remove-seeker-from-file-interface

Conversation

@lidel
Copy link
Copy Markdown
Member

@lidel lidel commented Mar 26, 2026

Not all File implementations can seek. ReaderFile wrapping an HTTP multipart stream and WebFile both returned ErrNotSupported at runtime, which caused interface-sniffing bugs in downstream consumers like go-car (ipfs/kubo#9361).

Not sure if this PR (removal of io.Seeker from File interface) is the "lesser evil", opening this mostly to have PR with code example.

With this PR seeking is now opt-in: implementations that support it (ReaderFile with a seekable reader, Symlink, ufsFile) still have a Seek method and callers type-assert to io.Seeker when needed.

  • files: remove io.Seeker from File interface
  • files: remove dummy WebFile.Seek that always returned ErrNotSupported
  • gateway: type-assert to io.Seeker before seeking in all handlers

Test plan

Not all File implementations can seek. ReaderFile wrapping an HTTP
multipart stream and WebFile both returned ErrNotSupported at runtime,
which caused interface-sniffing bugs in downstream consumers like
go-car (ipfs/kubo#9361).

Seeking is now opt-in: implementations that support it (ReaderFile
with a seekable reader, Symlink, ufsFile) still have a Seek method
and callers type-assert to io.Seeker when needed.

- files: remove io.Seeker from File interface
- files: remove dummy WebFile.Seek that always returned ErrNotSupported
- gateway: type-assert to io.Seeker before seeking in all handlers
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 26, 2026

Codecov Report

❌ Patch coverage is 20.00000% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.16%. Comparing base (b2b5d8a) to head (1cb4a9c).

Files with missing lines Patch % Lines
gateway/backend_blocks.go 14.28% 9 Missing and 3 partials ⚠️
gateway/backend_car.go 0.00% 4 Missing ⚠️
gateway/handler_block.go 20.00% 2 Missing and 2 partials ⚠️
gateway/handler_codec.go 40.00% 2 Missing and 1 partial ⚠️
gateway/backend_car_files.go 50.00% 1 Missing ⚠️

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1128      +/-   ##
==========================================
- Coverage   63.27%   63.16%   -0.11%     
==========================================
  Files         267      267              
  Lines       26848    26867      +19     
==========================================
- Hits        16987    16971      -16     
- Misses       8145     8171      +26     
- Partials     1716     1725       +9     
Files with missing lines Coverage Δ
files/webfile.go 73.91% <ø> (+3.07%) ⬆️
gateway/backend_car_files.go 57.93% <50.00%> (ø)
gateway/handler_codec.go 60.71% <40.00%> (-0.88%) ⬇️
gateway/backend_car.go 49.78% <0.00%> (-0.22%) ⬇️
gateway/handler_block.go 61.76% <20.00%> (-4.91%) ⬇️
gateway/backend_blocks.go 44.42% <14.28%> (-0.50%) ⬇️

... and 11 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

lidel added a commit to ipfs/kubo that referenced this pull request Mar 26, 2026
Update boxo to ipfs/boxo#1128 which removes io.Seeker from the
files.File interface. Callers that need seeking now type-assert
to io.Seeker.

- core/commands/cat: type-assert before seeking
- core/coreiface/tests: type-assert before seeking
@lidel lidel marked this pull request as ready for review March 26, 2026 20:23
@lidel lidel requested a review from a team as a code owner March 26, 2026 20:23
Copy link
Copy Markdown
Contributor

@gammazero gammazero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this all looks alright, but would like to any use of io.Seeker to io.Reader where ever possible.

Comment thread gateway/backend_car.go
Comment on lines +473 to +477
s, ok := f.(io.Seeker)
if !ok {
return nil, fmt.Errorf("file does not support seeking")
}
if _, err := s.Seek(from, io.SeekStart); err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to use io.Seeker here if from is never negative. Would it be OK to do a discarding read here?

Copy link
Copy Markdown
Member Author

@lidel lidel May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would think so, but we seem to have layers upon layers and sometimes it means seek happens more than once. 🙈

Tried the slice in 8d0b535, broke gateway-conformance, reverted in 1cb4a9c.

iiuc the reader returned from this site is handed to serveCodecRaw, which seeks it again via seekToStartOfFirstRange before serveContent. With Seek both seeks land at the same absolute offset (idempotent). With blockData[from:] the second seek lands inside the slice, so the body comes from offset 2*from. The seek here isn't redundant: the codec path assumes the reader spans the full block.

Cleaner fix would be to drop it and let serveCodecRaw/serveFile own range positioning, but that's a separate boxo/gateway refactor, lets keep this PR a minimal refactor.

@gammazero
Copy link
Copy Markdown
Contributor

Triage: OK with cleaning this up. @lidel will rebase and merge.

lidel added 4 commits May 15, 2026 01:17
resolve conflicts in CHANGELOG.md, gateway/handler_block.go, and
gateway/handler_codec.go. the handler conflicts both add an
io.Seeker type assertion now that files.File no longer embeds
io.Seeker, on top of the new size limit and roots-header logic
from origin/main.
show the before/after migration pattern for callers and link to
ipfs/kubo#11254 as a worked example of updating consumers.
lidel added a commit to ipfs/kubo that referenced this pull request May 14, 2026
Update boxo to ipfs/boxo#1128 which removes io.Seeker from the
files.File interface. Callers that need seeking now type-assert
to io.Seeker.

- core/commands/cat: type-assert before seeking
- core/coreiface/tests: type-assert before seeking
lidel added 2 commits May 15, 2026 01:43
at the non-DagPb terminal-entity site, blockData is already in
memory and from is non-negative once the negative case is converted
to an offset from start. drop the io.Seeker assertion and Seek call
in favor of NewBytesFile(blockData[from:]). clamp from to size to
preserve the previous bytes.Reader.Seek behavior of yielding an
empty body for out-of-range positive starts.
reverts 8d0b535 which broke gateway-conformance TestPlainCodec
range request cases (silent body corruption on JSON/CBOR codecs).
the codec path calls seekToStartOfFirstRange on the returned reader
again before serveContent, and that second Seek expects offsets in
the full block buffer, not a pre-sliced reader. add a comment
explaining the constraint so future readers don't make the same
mistake.
@lidel
Copy link
Copy Markdown
Member Author

lidel commented May 14, 2026

Merging, Sharness passes in ipfs/kubo#11254, will start passing in boxo as soon we land that PR too.

@lidel lidel merged commit c870823 into main May 15, 2026
29 of 31 checks passed
@lidel lidel deleted the fix/files-remove-seeker-from-file-interface branch May 15, 2026 00:02
lidel added a commit to ipfs/kubo that referenced this pull request May 15, 2026
Update boxo to ipfs/boxo#1128 which removes io.Seeker from the
files.File interface. Callers that need seeking now type-assert
to io.Seeker.

- core/commands/cat: type-assert before seeking
- core/coreiface/tests: type-assert before seeking
lidel added a commit to ipfs/kubo that referenced this pull request May 15, 2026
Update boxo to ipfs/boxo#1128 which removes io.Seeker from the
files.File interface. Callers that need seeking now type-assert
to io.Seeker.

- core/commands/cat: type-assert before seeking
- core/coreiface/tests: type-assert before seeking
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants