Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0fd1371da |
@@ -30,11 +30,8 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of
|
||||
link is opened without following it and given to the netwatch user itself, so
|
||||
the server can still follow one in a sticky directory that anyone can write
|
||||
to. Other files that are neither directories, regular files nor symbolic links
|
||||
keep their owner. Setting the owner on an open file or link needs Linux, the
|
||||
system of the image; on any other system the package still builds, and
|
||||
`prepare-data-dir` fails at the first regular file or symbolic link. The test
|
||||
that runs as root hard-links a file from outside the volume into it and finds
|
||||
its owner unchanged
|
||||
keep their owner. The test that runs as root hard-links a file from outside
|
||||
the volume into it and finds its owner unchanged
|
||||
- 2026-10-18: after an interval change, the summary's Reachable slot reads
|
||||
`--/--` in white, as at page load, until the second round since the change
|
||||
ends ([#140](https://git.eeqj.de/sneak/netwatch/issues/140)). It used to read
|
||||
|
||||
@@ -3,11 +3,14 @@ package reportbuf
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// ErrDataDirOutsideVolume is returned by PrepareDataDir for a DATA_DIR
|
||||
@@ -24,9 +27,7 @@ var ErrDataDirOutsideVolume = errors.New(
|
||||
// with a second name (hard link), as that name could be outside volume,
|
||||
// and any other file, such as a device (see chownAll). dir must be
|
||||
// volume or a path below it, with no '.', '..', empty part or '/' at
|
||||
// the end. It gives a regular file or symbolic link to owner only on
|
||||
// Linux, the system of the image; on any other it fails at the first
|
||||
// one (see chownFile).
|
||||
// the end.
|
||||
//
|
||||
// bin/entrypoint.sh runs this as root, which would follow a symbolic
|
||||
// link anywhere, so every step goes through an os.Root opened on
|
||||
@@ -154,6 +155,41 @@ func chownDir(root *os.Root, name string, uid, gid int) ([]fs.DirEntry, error) {
|
||||
return dir.ReadDir(-1)
|
||||
}
|
||||
|
||||
// chownFile gives name, a regular file or symbolic link inside root, to
|
||||
// uid and gid, unless it has a second name (hard link): all the names
|
||||
// of a file share its owner, and the other could be outside root. It
|
||||
// opens name through root with O_PATH, which reads nothing from the
|
||||
// file; os.Root adds O_NOFOLLOW, and the two together open a symbolic
|
||||
// link itself, never what it points to. It reads the number of names
|
||||
// from the open file and gives that file to them, so a hard link
|
||||
// swapped in at name is caught too. It logs a file it leaves.
|
||||
func chownFile(root *os.Root, name string, uid, gid int) error {
|
||||
file, err := root.OpenFile(name, unix.O_PATH, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// File.Chown would fail: fchown refuses a file opened with O_PATH.
|
||||
// Fchownat with an empty name and AT_EMPTY_PATH acts on the open
|
||||
// file instead.
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if ok && stat.Nlink == 1 {
|
||||
return unix.Fchownat(int(file.Fd()), "", uid, gid, unix.AT_EMPTY_PATH)
|
||||
}
|
||||
|
||||
slog.Warn("owner not changed: file has a second name (hard link)",
|
||||
"path", file.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// chmodDir gives name, a directory inside root, the mode the server
|
||||
// gives a directory it creates. Root.Chmod would not hold: on Linux it
|
||||
// checks that name is not a symbolic link, then sets the mode by name,
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
//go:build linux
|
||||
|
||||
package reportbuf
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// chownFile gives name, a regular file or symbolic link inside root, to
|
||||
// uid and gid, unless it has a second name (hard link): all the names
|
||||
// of a file share its owner, and the other could be outside root. It
|
||||
// opens name through root with O_PATH, which reads nothing from the
|
||||
// file; os.Root adds O_NOFOLLOW, and the two together open a symbolic
|
||||
// link itself, never what it points to. It reads the number of names
|
||||
// from the open file and gives that file to them, so a hard link
|
||||
// swapped in at name is caught too. It logs a file it leaves.
|
||||
//
|
||||
// This file is built only on Linux, because O_PATH and AT_EMPTY_PATH
|
||||
// exist only there; prepare-data-dir runs only in the Linux image. On
|
||||
// any other system chownFile returns an error (see datadir_other.go).
|
||||
func chownFile(root *os.Root, name string, uid, gid int) error {
|
||||
file, err := root.OpenFile(name, unix.O_PATH, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// File.Chown would fail: fchown refuses a file opened with O_PATH.
|
||||
// Fchownat with an empty name and AT_EMPTY_PATH acts on the open
|
||||
// file instead.
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if ok && stat.Nlink == 1 {
|
||||
return unix.Fchownat(int(file.Fd()), "", uid, gid, unix.AT_EMPTY_PATH)
|
||||
}
|
||||
|
||||
slog.Warn("owner not changed: file has a second name (hard link)",
|
||||
"path", file.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build !linux
|
||||
|
||||
package reportbuf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// chownFile returns an error on every system but Linux. Giving a
|
||||
// regular file or symbolic link to a user on the open file, never by
|
||||
// name, needs O_PATH and AT_EMPTY_PATH, which only Linux has (see
|
||||
// datadir_linux.go), and prepare-data-dir runs only in the Linux
|
||||
// image. This file lets the package build elsewhere, such as on macOS,
|
||||
// where a developer runs make build and make run.
|
||||
func chownFile(_ *os.Root, name string, _, _ int) error {
|
||||
return &fs.PathError{Op: "chown", Path: name, Err: errors.ErrUnsupported}
|
||||
}
|
||||
Reference in New Issue
Block a user