@ 261101.7

261021.24 · e6a571cefd9377bad7daa04fcae550796e746185

Fix the issues a review of the server found

- --persist on a server's only tree removed the plain lock before
joining, so the server looked gone and the start failed with E98.
The lock now goes only once the new link is in place.
- Each join makes a registry link of a new name, so the server tells a
re-join from its own link even when the new link reuses the inode.
The name is cut short to stay under NAME_MAX, so deep directories
can be served, and a link that fails to register is removed.
- A share's search no longer names dot entries its listing hides.
- serve waits for its loop and signal handler before closing the
registry, which they may still lock or unlock: a data race that
could release another registry's flock. The git cache is reset in
place rather than replaced under an earlier server's goroutines.
- A persistent lock counts as served only if the running server wrote
it, so a lock kept from before a reboot no longer reports a URL that
another tree has taken.
- A secret token spelled with escapes no longer reaches the log.
- A share's DNS lookup runs before the registry is locked, so it holds
up no other www.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

reading declarations

Changes to lock_test.go. All files

lock_test.go

@@ -5,6 +5,7 @@ import ( 	"net/netip" 	"os" 	"path/filepath"+	"strings" 	"testing" ) @@ -91,7 +92,34 @@ func TestRemoveSparesReplacedLock(t *testing.T) { }  func TestLinkNameEscapes(t *testing.T) {-	if got := linkName("/home/u/100%/a"); got != "%2Fhome%2Fu%2F100%25%2Fa" {+	got := linkName("/home/u/100%/a")+	if !strings.HasPrefix(got, "%2Fhome%2Fu%2F100%25%2Fa~") || len(got) != len("%2Fhome%2Fu%2F100%25%2Fa~")+8 { 		t.Errorf("link name %q", got) 	}+	if linkName("/a") == linkName("/a") {+		t.Error("two joins of a tree got links of one name")+	}+}++// A deep directory's link still fits in a file name.+func TestLinkNameFitsDeepDir(t *testing.T) {+	dir := "/" + strings.Repeat("deep/", 60)+	if n := len(linkName(dir)); n > 255 {+		t.Errorf("link name of %d bytes", n)+	}+	reg := &registry{dir: t.TempDir()}+	if _, _, err := reg.add(filepath.Join(dir, "127.31.1.1-8080.lock"), ""); err != nil {+		t.Fatal(err)+	}+}++// hasLink reports whether the registry in regDir links a lock in dir.+func hasLink(regDir, dir string) bool {+	links, _ := readLinks(regDir)+	for _, target := range links {+		if lockPath, _ := splitTarget(target); filepath.Dir(lockPath) == dir {+			return true+		}+	}+	return false }