tarfs: DirEntry.Type returns the full mode instead of the type bits

Open Beginner friendly
#2,495 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
go

Research direction

Start with the two Type methods shown in the tarfs implementation and the fs.DirEntry/FileInfo contract. Reproduce the setuid example, then verify both implementations return only type bits while Mode remains unchanged. Treat the Open(".")/fstest.TestFS issue as a separate conformance decision.

Written by the indexing model from the issue text.

Description

fs.DirEntry.Type is documented to return "the type bits for the entry -- the type bits are those returned by [FileMode.Type]". tarfs returns the full mode, permission bits included, from both of its DirEntry implementations:

func (e Entry) Type() fs.FileMode  { return e.fi.Mode() }
func (e *entry) Type() fs.FileMode { return e.fileMode }

*entry values are what ReadDir hands back (they are stored in fsys.dirs as fs.DirEntry), so every ReadDir result violates the contract. fs.WalkDir also calls Type() on them.

Reproduction

An archive with a setuid regular file at usr/setuid (mode 0o4755):

ReadDir entry "setuid"  Type()=urwxr-xr-x  Mode()=urwxr-xr-x  Mode().Type()=----------
  contract says Type() should equal Mode().Type(): false

For a regular file Type() should be 0 (no type bits set). Callers that switch on d.Type() to classify entries, or compare it against fs.ModeDir/fs.ModeSymlink, get the wrong answer for any member with permission bits — which is every member.

Suggested fix

func (e *entry) Type() fs.FileMode { return e.fileMode.Type() }

and the same on Entry.Type. Both should change together; fixing only one would leave the two implementations disagreeing.

Mode() is unaffected and should keep returning the full mode, which is what fs.FileInfo.Mode documents.

Related, found by the same probe

fstest.TestFS does not pass against a tarfs.FS:

.: Open: file does not exist
expected but not found: usr/setuid

Stat(".") synthesizes a root entry but Open(".") does not, so the conformance walk cannot start. That is a separate gap from the Type() issue and may well be intentional given how the package is used, but it means fs.WalkDir from the root does not work either. Worth deciding whether full fs.FS conformance is a goal for this package; if it is, fstest.TestFS would be a good addition to the test suite and would have caught the Type() bug on its own.

Impact

Low today. Both Type() implementations have had this behavior for as long as the package has existed, and no in-tree consumer appears to switch on d.Type() -- pkg/apk/apk reads headers rather than walking the FS. Filing it because it is a silent contract violation on an exported type, so a future caller using the standard fs.DirEntry idiom would get wrong results with no error.

Dominant language
Go
Stars
1.7k
Forks
228
Avg merge
1d 3h
Merged PRs (30d)
62

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from chainguard-dev/apko

All issues in chainguard-dev/apko

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.