Skip to content

Peer deletion leaves tracked dangling symlinks on disk #331

Open

Description

@isac322

A peer can delete a tracked dangling symlink, but another device leaves the link on disk. This also occurs on the base commit before #329.

Reproduction

Run this test in internal/syncer using its existing two-device helpers:

func TestReproPeerDanglingSymlinkDeletion(t *testing.T) {
    be := sharedRemote(t)
    a := newDevice(t, "deva", be)
    b := newDevice(t, "devb", be)
    const name = "dangling-link"
    if err := os.Symlink("absent-target", filepath.Join(a.Folder, name)); err != nil { t.Fatal(err) }
    cycle(t, a)
    cycle(t, b)
    if _, err := os.Readlink(filepath.Join(b.Folder, name)); err != nil { t.Fatal(err) }
    if err := os.Remove(filepath.Join(a.Folder, name)); err != nil { t.Fatal(err) }
    cycle(t, a)
    cycle(t, b)
    if _, err := os.Lstat(filepath.Join(b.Folder, name)); !os.IsNotExist(err) {
        t.Fatalf("peer deletion left dangling link: %v", err)
    }
}

Imports: os, path/filepath, testing.

Command: go test ./internal/syncer -count=1 -run '^TestReproPeerDanglingSymlinkDeletion$' -timeout=2m.

Observed on Darwin arm64: the test fails with peer deletion left dangling link: <nil> on both base eaa6e0d and PR revision 77b1871. Before deletion, the receiving device holds the expected link. After deletion, it still exists.

Cause and scope

The deletion loop in Session.materialize resolves the final path through store.UnderRoot and uses os.Stat before unlinking. A dangling link cannot be resolved, and following a live link compares target metadata instead of link metadata. The dangling-link case drops the cache entry without removing the filesystem entry.

The deletion block is unchanged by #329. Track its repair separately: use link-aware containment and fingerprint checks while preserving writable local edits and never following symlink ancestors. Remote symlink creation, deletion, and concurrent local link replacement need two-device regression coverage.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions