Skip to content

fix(inode): report symlink size as the length of its target - #4943

Merged
anushka567 merged 2 commits into
GoogleCloudPlatform:masterfrom
vyncint:fix-symlink-size-2273
Jul 30, 2026
Merged

anushka567 merged 2 commits into
GoogleCloudPlatform:masterfrom
vyncint:fix-symlink-size-2273

Conversation

@vyncint

@vyncint vyncint commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Description

SymlinkInode.Attributes never assigned its size return value, so it returned the zero value and stat reported a size of 0 for every symlink.

func (s *SymlinkInode) Attributes(
	ctx context.Context, clobberedCheck bool) (size uint64, mtime time.Time, nlink uint32, err error) {
	mtime = s.mtime
	nlink = 1
	return
}

stat(2) requires st_size of a symlink to be the length of the pathname it contains, without a terminating null byte. The intended semantics were already documented in this same file, on UpdateSize a few lines above:

// The size of a symlink is its target's length, not the backing object's size.

So the contract was written down; Attributes just did not implement it. For a legacy symlink the backing object is empty, which is why the reported size came out as 0 rather than as some other wrong number.

The fix is one line: size = uint64(len(s.target)).

Scope check: fs.getAttributes is the only caller of Attributes, and it assigns the value straight to attr.Size. The rename path reads Source() (the backing object's size) rather than Attributes, so it is unaffected. The change is limited to the size reported to the kernel.

Link to the issue in case of a bug fix.

#2273

I posted the root-cause analysis there first, per the dev guide, including the open question below. Happy to close this PR if you would rather handle it yourselves or scope it differently.

Testing details

  1. Manual - NA. Verified through the composite tests below, which exercise a real mount rather than only the inode layer.
  2. Unit tests - updated TestAttributes, which previously asserted uint64(0). Added TestAttributesSizeIsTargetLength, covering the legacy metadata representation with a zero-size backing object, a non-zero-size backing object (so the assertion cannot pass by coincidence), an empty target, and a multi-byte target. Added TestAttributesSizeForStandardSymlink for the content-based representation.
  3. Integration tests - not run; I do not have a test bucket. The composite suite was run instead: ./internal/fs/... passes, including a new SymlinkTest.LinkSizeIsTargetLength that creates symlinks through the mount and checks Lstat reports the target length.

As a negative control I reverted the one-line fix and confirmed all three new tests fail with actual: 0x0, so they do test the change rather than pass incidentally.

Six packages fail in my container (benchmarks/internal/percentile, internal/block, internal/bufferedread, internal/cache/file, internal/cache/util, internal/gcsx). I checked these against a clean checkout and the failing set is identical, so they are environmental - the container runs as root, which defeats the permission-based cases - and unrelated to this change.

Any backward incompatible change? If so, please explain.

Yes, in observable behaviour, and it is worth an explicit decision rather than being waved through.

stat on a symlink now reports the target length instead of 0. Anything that reads st_size for a symlink sees a different number - though 0 was never a value a POSIX-conforming caller could use, and tools like tar, rsync and ls -l expect the target length.

Concretely, four existing assertions pinned the old value and are updated here:

  • internal/fs/inode/symlink_test.go - TestAttributes
  • internal/fs/local_modifications_test.go - SymlinkTest.CreateLink, twice, via Lstat and via the readdir entry
  • internal/fs/caching_test.go - CachingWithImplicitDirsTest.SymlinksWork
  • internal/fs/foreign_modifications_test.go - ForeignModsTest.Symlink

My reading is that these were written to match the implementation rather than to encode a deliberate decision, but that judgement is yours. The GCS object size assertion in CreateLink (ExpectEq(0, m.Size)) is left alone - that one is correctly 0, since a legacy symlink's target lives in metadata and the object really is empty.

@vyncint
vyncint requested a review from a team as a code owner July 26, 2026 22:41
@vyncint
vyncint requested a review from charith87 July 26, 2026 22:41

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request updates the Attributes method of SymlinkInode to return the length of the target path as the symlink size, aligning with POSIX requirements. It also updates existing tests and adds new test cases to verify that the symlink size matches the target path length for both legacy and standard symlinks. I have no feedback to provide as there are no review comments.

@charith87
charith87 requested review from anushka567 and removed request for charith87 July 27, 2026 05:42
@anushka567 anushka567 added the execute-integration-tests Run only integration tests label Jul 27, 2026
@anushka567

Copy link
Copy Markdown
Member

@vyncint Can you please push an empty commit to retrigger CI (kokoro specifically) ? Thanks!

@vyncint

vyncint commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Done — pushed 5a7a68e, an empty commit (identical tree to 84c3180, 0 files changed), so the diff is untouched at 113/-6 across the same 5 files.

The GitHub Actions checks have re-triggered on the new head. Kokoro has not reported yet; if it stays quiet, note the PR carries execute-integration-tests but not kokoro:run — I left that one alone since applying it is yours to decide. Happy to push another if you need it.

@anushka567 anushka567 added the kokoro:run Testing kokoro run label Jul 28, 2026
@kokoro-team kokoro-team removed the kokoro:run Testing kokoro run label Jul 28, 2026
@vyncint

vyncint commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi @anushka567, Kokoro is red, but the log is on fusion2. Could you paste the failing test name?

Checked from outside first: no e2e test asserts a symlink's st_size through the mount, and the one that looks like a hit ("Legacy symlink size should be 0" in symlink_suites_test.go) reads the GCS object's own size via objHandle.Attrs, not what gcsfuse reports. linux-tests is green.

4 of 7 recent PRs that actually ran this suite also failed, so it may be a flake, but I'd rather see the name than assume. Happy to push another empty commit if you'd prefer a re-run.

@anushka567

anushka567 commented Jul 28, 2026 •

Copy link
Copy Markdown
Member

@vyncint This was a recent regression for GCSFuse CI and the fix was merged a while back (commit- f3eecbc22588ef1da2b5113ef66111efc072fe17). Can you please rebase the branch on master to pick the changes, thanks!

@anushka567

Copy link
Copy Markdown
Member

fyi, the failing test package is stale_handle and the failure is unrelated to the changes made in this PR.

@vyncint vyncint closed this Jul 28, 2026
@vyncint
vyncint force-pushed the fix-symlink-size-2273 branch from 5a7a68e to 207124b Compare July 28, 2026 08:42
SymlinkInode.Attributes never assigned its size return value, so stat reported
a size of 0 for every symlink. stat(2) requires st_size of a symlink to be the
length of the pathname it contains, and the intended semantics were already
documented on UpdateSize just above ("The size of a symlink is its target's
length, not the backing object's size") - Attributes simply did not implement
them. For a legacy symlink the backing object is empty, which is why the
reported size was zero rather than merely wrong.

getAttributes is the only caller of Attributes, and the rename path reads
Source() rather than Attributes, so the change is scoped to the size reported
to the kernel.

Four existing assertions pinned the old value and have been updated, in
symlink_test.go, local_modifications_test.go, caching_test.go and
foreign_modifications_test.go.

Issue: GoogleCloudPlatform#2273
@vyncint vyncint reopened this Jul 28, 2026
@vyncint

vyncint commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi @anushka567, I rebased onto master — bb68de1 now includes f3eecbc. Single commit, diff unchanged at +113/-6 across the same 5 files.

Heads up: the force-push briefly landed on master's tip, which auto-closed the PR. Reopened right away — sorry for the notification noise.

The ci workflow is at action_required, so it needs your approval to run.

The rebase landed while the PR was briefly closed, so no synchronize
event reached Kokoro. Tree is identical to bb68de1.
@vyncint

vyncint commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi @anushka567, could you help me apply kokoro:run again? The label gets consumed on each run, and I can't apply it myself.

@anushka567 anushka567 added the kokoro:run Testing kokoro run label Jul 28, 2026
@kokoro-team kokoro-team removed the kokoro:run Testing kokoro run label Jul 28, 2026
@vyncint

vyncint commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Hi @anushka567,

Thanks for applying the label. Everything is green now, including Kokoro.

The rebase picked up f3eecbc, and the stale_handle failure is gone. The diff is
unchanged at +113/-6 across the same 5 files.

Ready for review whenever you have a moment.

@anushka567 anushka567 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Will need codeowner approval for merge.

@charith87 charith87 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Relying on Anushka's Review

@anushka567
anushka567 merged commit bba6207 into GoogleCloudPlatform:master Jul 30, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

execute-integration-tests Run only integration tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants