vendor latest c/{buildah,common,image,storage}

Make sure everything passes for rc2.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger
2025-01-27 12:41:14 +01:00
parent c76c13f179
commit 141bd613b8
96 changed files with 2712 additions and 1225 deletions

View File

@@ -0,0 +1,39 @@
package open
import (
"errors"
"fmt"
"syscall"
)
// InChroot opens the file at `path` after chrooting to `root` and then
// changing its working directory to `wd`. Both `wd` and `path` are evaluated
// in the chroot.
// Returns a file handle, an Errno value if there was an error and the
// underlying error was a standard library error code, and a non-empty error if
// one was detected.
func InChroot(root, wd, path string, mode int, perm uint32) (fd int, errno syscall.Errno, err error) {
requests := requests{
Root: root,
Wd: wd,
Open: []request{
{
Path: path,
Mode: mode,
Perms: perm,
},
},
}
results := inChroot(requests)
if len(results.Open) != 1 {
return -1, 0, fmt.Errorf("got %d results back instead of 1", len(results.Open))
}
if results.Open[0].Err != "" {
if results.Open[0].Errno != 0 {
err = fmt.Errorf("%s: %w", results.Open[0].Err, results.Open[0].Errno)
} else {
err = errors.New(results.Open[0].Err)
}
}
return int(results.Open[0].Fd), results.Open[0].Errno, err
}

View File

@@ -0,0 +1,88 @@
package open
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"github.com/containers/storage/pkg/reexec"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
const (
bindFdToPathCommand = "buildah-bind-fd-to-path"
)
func init() {
reexec.Register(bindFdToPathCommand, bindFdToPathMain)
}
// BindFdToPath creates a bind mount from the open file (which is actually a
// directory) to the specified location. If it succeeds, the caller will need
// to unmount the targetPath when it's finished using it. Regardless, it
// closes the passed-in descriptor.
func BindFdToPath(fd uintptr, targetPath string) error {
f := os.NewFile(fd, "passed-in directory descriptor")
defer func() {
if err := f.Close(); err != nil {
logrus.Debugf("closing descriptor %d after attempting to bind to %q: %v", fd, targetPath, err)
}
}()
pipeReader, pipeWriter, err := os.Pipe()
if err != nil {
return err
}
cmd := reexec.Command(bindFdToPathCommand)
cmd.Stdin = pipeReader
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout, cmd.Stderr = &stdout, &stderr
cmd.ExtraFiles = append(cmd.ExtraFiles, f)
err = cmd.Start()
pipeReader.Close()
if err != nil {
pipeWriter.Close()
return fmt.Errorf("starting child: %w", err)
}
encoder := json.NewEncoder(pipeWriter)
if err := encoder.Encode(&targetPath); err != nil {
return fmt.Errorf("sending target path to child: %w", err)
}
pipeWriter.Close()
err = cmd.Wait()
trimmedOutput := strings.TrimSpace(stdout.String()) + strings.TrimSpace(stderr.String())
if err != nil {
if len(trimmedOutput) > 0 {
err = fmt.Errorf("%s: %w", trimmedOutput, err)
}
} else {
if len(trimmedOutput) > 0 {
err = errors.New(trimmedOutput)
}
}
return err
}
func bindFdToPathMain() {
var targetPath string
decoder := json.NewDecoder(os.Stdin)
if err := decoder.Decode(&targetPath); err != nil {
fmt.Fprintf(os.Stderr, "error decoding target path")
os.Exit(1)
}
if err := unix.Fchdir(3); err != nil {
fmt.Fprintf(os.Stderr, "fchdir(): %v", err)
os.Exit(1)
}
if err := unix.Mount(".", targetPath, "bind", unix.MS_BIND, ""); err != nil {
fmt.Fprintf(os.Stderr, "bind-mounting passed-in directory to %q: %v", targetPath, err)
os.Exit(1)
}
os.Exit(0)
}

View File

@@ -0,0 +1,28 @@
package open
import (
"syscall"
)
type request struct {
Path string
Mode int
Perms uint32
}
type requests struct {
Root string
Wd string
Open []request
}
type result struct {
Fd uintptr // as returned by open()
Err string // if err was not `nil`, err.Error()
Errno syscall.Errno // if err was not `nil` and included a syscall.Errno, its value
}
type results struct {
Err string
Open []result
}

View File

@@ -0,0 +1,168 @@
//go:build linux || freebsd || darwin
package open
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"syscall"
"github.com/containers/storage/pkg/reexec"
"golang.org/x/sys/unix"
)
const (
inChrootCommand = "buildah-open-in-chroot"
)
func init() {
reexec.Register(inChrootCommand, inChrootMain)
}
func inChroot(requests requests) results {
sock, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0)
if err != nil {
return results{Err: fmt.Errorf("creating socket pair: %w", err).Error()}
}
parentSock := sock[0]
childSock := sock[1]
parentEnd := os.NewFile(uintptr(parentSock), "parent end of socket pair")
childEnd := os.NewFile(uintptr(childSock), "child end of socket pair")
cmd := reexec.Command(inChrootCommand)
cmd.ExtraFiles = append(cmd.ExtraFiles, childEnd)
err = cmd.Start()
childEnd.Close()
defer parentEnd.Close()
if err != nil {
return results{Err: err.Error()}
}
encoder := json.NewEncoder(parentEnd)
if err := encoder.Encode(&requests); err != nil {
return results{Err: fmt.Errorf("sending request down socket: %w", err).Error()}
}
if err := unix.Shutdown(parentSock, unix.SHUT_WR); err != nil {
return results{Err: fmt.Errorf("finishing sending request down socket: %w", err).Error()}
}
b := make([]byte, 65536)
oob := make([]byte, 65536)
n, oobn, _, _, err := unix.Recvmsg(parentSock, b, oob, 0)
if err != nil {
return results{Err: fmt.Errorf("receiving message: %w", err).Error()}
}
if err := unix.Shutdown(parentSock, unix.SHUT_RD); err != nil {
return results{Err: fmt.Errorf("finishing socket: %w", err).Error()}
}
if n > len(b) {
return results{Err: fmt.Errorf("too much regular data: %d > %d", n, len(b)).Error()}
}
if oobn > len(oob) {
return results{Err: fmt.Errorf("too much OOB data: %d > %d", oobn, len(oob)).Error()}
}
scms, err := unix.ParseSocketControlMessage(oob[:oobn])
if err != nil {
return results{Err: fmt.Errorf("parsing control message: %w", err).Error()}
}
var receivedFds []int
for i := range scms {
fds, err := unix.ParseUnixRights(&scms[i])
if err != nil {
return results{Err: fmt.Errorf("parsing rights message %d: %w", i, err).Error()}
}
receivedFds = append(receivedFds, fds...)
}
decoder := json.NewDecoder(bytes.NewReader(b[:n]))
var result results
if err := decoder.Decode(&result); err != nil {
return results{Err: fmt.Errorf("decoding results: %w", err).Error()}
}
j := 0
for i := range result.Open {
if result.Open[i].Err == "" {
if j >= len(receivedFds) {
for _, fd := range receivedFds {
unix.Close(fd)
}
return results{Err: fmt.Errorf("didn't receive enough FDs").Error()}
}
result.Open[i].Fd = uintptr(receivedFds[j])
j++
}
}
return result
}
func inChrootMain() {
var theseRequests requests
var theseResults results
sockFd := 3
sock := os.NewFile(uintptr(sockFd), "socket connection to parent process")
defer sock.Close()
encoder := json.NewEncoder(sock)
decoder := json.NewDecoder(sock)
if err := decoder.Decode(&theseRequests); err != nil {
if err := encoder.Encode(results{Err: fmt.Errorf("decoding request: %w", err).Error()}); err != nil {
os.Exit(1)
}
}
if theseRequests.Root != "" {
if err := os.Chdir(theseRequests.Root); err != nil {
if err := encoder.Encode(results{Err: fmt.Errorf("changing to %q: %w", theseRequests.Root, err).Error()}); err != nil {
os.Exit(1)
}
os.Exit(1)
}
if err := unix.Chroot(theseRequests.Root); err != nil {
if err := encoder.Encode(results{Err: fmt.Errorf("chrooting to %q: %w", theseRequests.Root, err).Error()}); err != nil {
os.Exit(1)
}
os.Exit(1)
}
if err := os.Chdir("/"); err != nil {
if err := encoder.Encode(results{Err: fmt.Errorf("changing to new root: %w", err).Error()}); err != nil {
os.Exit(1)
}
os.Exit(1)
}
}
if theseRequests.Wd != "" {
if err := os.Chdir(theseRequests.Wd); err != nil {
if err := encoder.Encode(results{Err: fmt.Errorf("changing to %q in chroot: %w", theseRequests.Wd, err).Error()}); err != nil {
os.Exit(1)
}
os.Exit(1)
}
}
var fds []int
for _, request := range theseRequests.Open {
fd, err := unix.Open(request.Path, request.Mode, request.Perms)
thisResult := result{Fd: uintptr(fd)}
if err == nil {
fds = append(fds, fd)
} else {
var errno syscall.Errno
thisResult.Err = err.Error()
if errors.As(err, &errno) {
thisResult.Errno = errno
}
}
theseResults.Open = append(theseResults.Open, thisResult)
}
rights := unix.UnixRights(fds...)
inband, err := json.Marshal(&theseResults)
if err != nil {
if err := encoder.Encode(results{Err: fmt.Errorf("sending response: %w", err).Error()}); err != nil {
os.Exit(1)
}
os.Exit(1)
}
if err := unix.Sendmsg(sockFd, inband, rights, nil, 0); err != nil {
if err := encoder.Encode(results{Err: fmt.Errorf("sending response: %w", err).Error()}); err != nil {
os.Exit(1)
}
os.Exit(1)
}
os.Exit(0)
}

View File

@@ -0,0 +1,7 @@
//go:build !linux && !freebsd && !darwin
package open
func inChroot(requests requests) results {
return results{Err: "open-in-chroot not available on this platform"}
}