prepare-data-dir leaves files with a second name to their owner (closes #143)
check / check (push) Waiting to run

A hard link in /data shares its owner with a file that can be anywhere
on the same filesystem, so chowning it gave that file to the netwatch
user. A regular file with more than one name now keeps its owner and
its path is logged.

Owners are now set on the opened directory or file after checking it,
as chmodDir sets modes, so a hard link swapped in afterwards is caught.
Symbolic links and other files that are neither directories nor
regular files are no longer given away: they cannot be opened safely,
and setting their owner by name could reach a hard link swapped in.

Model: opus-5-5
This commit is contained in:
2026-10-18 20:40:43 +00:00
parent 2861be1c4d
commit 14e4ff908d
4 changed files with 119 additions and 44 deletions
+12 -6
View File
@@ -254,10 +254,11 @@ func TestPrepareDataDirTakesDirectoryNamedInLatin1(t *testing.T) {
requireOwner(t, filepath.Join(latin1, "f"), uint32(uid), uint32(gid))
}
// TestPrepareDataDirGivesVolumeToOwner gives everything in the volume
// to a uid and gid that own nothing, which only root can do. A
// symbolic link in the volume to a directory outside it is given to
// them itself; what it points to is left as it was.
// TestPrepareDataDirGivesVolumeToOwner gives the directories and
// regular files in the volume to a uid and gid that own nothing, which
// only root can do. A symbolic link in the volume to a directory
// outside it, and a hard link to a file outside it, are left as they
// were, and so is what they lead to.
func TestPrepareDataDirGivesVolumeToOwner(t *testing.T) {
t.Parallel()
@@ -289,6 +290,11 @@ func TestPrepareDataDirGivesVolumeToOwner(t *testing.T) {
t.Fatal(err)
}
err = os.Link(filepath.Join(outside, "f"), filepath.Join(old, "hardlink"))
if err != nil {
t.Fatal(err)
}
err = reportbuf.PrepareDataDir(volume, filepath.Join(volume, reports),
&user.User{Uid: "4242", Gid: "4343"})
if err != nil {
@@ -296,12 +302,12 @@ func TestPrepareDataDirGivesVolumeToOwner(t *testing.T) {
}
for _, path := range []string{
volume, filepath.Join(volume, reports), old,
filepath.Join(old, "f"), filepath.Join(old, "link"),
volume, filepath.Join(volume, reports), old, filepath.Join(old, "f"),
} {
requireOwner(t, path, 4242, 4343)
}
requireOwner(t, filepath.Join(old, "link"), 0, 0)
requireOwner(t, outside, 0, 0)
requireOwner(t, filepath.Join(outside, "f"), 0, 0)
}