Repository navigation
fix(inode): report symlink size as the length of its target - #4943
Conversation
There was a problem hiding this comment.
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.
|
@vyncint Can you please push an empty commit to retrigger CI (kokoro specifically) ? Thanks! |
|
Done — pushed The GitHub Actions checks have re-triggered on the new head. Kokoro has not reported yet; if it stays quiet, note the PR carries |
|
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. |
|
@vyncint This was a recent regression for GCSFuse CI and the fix was merged a while back (commit- |
|
fyi, the failing test package is stale_handle and the failure is unrelated to the changes made in this PR. |
5a7a68e to
207124b
Compare
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
|
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.
|
Hi @anushka567, could you help me apply kokoro:run again? The label gets consumed on each run, and I can't apply it myself. |
|
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 Ready for review whenever you have a moment. |
anushka567
left a comment
There was a problem hiding this comment.
LGTM. Will need codeowner approval for merge.
charith87
left a comment
There was a problem hiding this comment.
Relying on Anushka's Review
Description
SymlinkInode.Attributesnever assigned itssizereturn value, so it returned the zero value andstatreported a size of 0 for every symlink.stat(2)requiresst_sizeof 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, onUpdateSizea 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;
Attributesjust 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.getAttributesis the only caller ofAttributes, and it assigns the value straight toattr.Size. The rename path readsSource()(the backing object's size) rather thanAttributes, 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
TestAttributes, which previously asserteduint64(0). AddedTestAttributesSizeIsTargetLength, 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. AddedTestAttributesSizeForStandardSymlinkfor the content-based representation../internal/fs/...passes, including a newSymlinkTest.LinkSizeIsTargetLengththat creates symlinks through the mount and checksLstatreports 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.
staton a symlink now reports the target length instead of 0. Anything that readsst_sizefor a symlink sees a different number - though 0 was never a value a POSIX-conforming caller could use, and tools liketar,rsyncandls -lexpect the target length.Concretely, four existing assertions pinned the old value and are updated here:
internal/fs/inode/symlink_test.go-TestAttributesinternal/fs/local_modifications_test.go-SymlinkTest.CreateLink, twice, viaLstatand via the readdir entryinternal/fs/caching_test.go-CachingWithImplicitDirsTest.SymlinksWorkinternal/fs/foreign_modifications_test.go-ForeignModsTest.SymlinkMy 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.