//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 }