diff --git a/common/libimage/load.go b/common/libimage/load.go index 598bf39cdc..cb36b8f709 100644 --- a/common/libimage/load.go +++ b/common/libimage/load.go @@ -16,6 +16,7 @@ import ( "go.podman.io/image/v5/transports" "go.podman.io/image/v5/types" "go.podman.io/storage/pkg/fileutils" + "golang.org/x/sys/unix" ) type LoadOptions struct { @@ -110,6 +111,12 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ( return loadedImages, err } logrus.Debugf("Error loading %s (%s): %v", path, transportName, err) + + if errors.Is(err, unix.ENOSPC) { + // %.0w makes e visible to error.Unwrap() without including any text + return nil, fmt.Errorf("no space left on device%.0w", err) + } + loadErrors = append(loadErrors, fmt.Errorf("%s: %v", transportName, err)) } diff --git a/storage/pkg/chrootarchive/archive_unix.go b/storage/pkg/chrootarchive/archive_unix.go index bc649bbbe6..725a172347 100644 --- a/storage/pkg/chrootarchive/archive_unix.go +++ b/storage/pkg/chrootarchive/archive_unix.go @@ -10,6 +10,7 @@ import ( "io" "io/fs" "os" + "os/exec" "path/filepath" "runtime" "strings" @@ -82,6 +83,9 @@ func untar() { } if err := archive.Unpack(os.Stdin, dst, &options); err != nil { + if errors.Is(err, unix.ENOSPC) { + os.Exit(2) + } fatal(err) } // fully consume stdin in case it is zero padded @@ -155,6 +159,13 @@ func invokeUnpack(decompressedArchive io.Reader, dest *unpackDestination, option w.Close() if err := cmd.Wait(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + status := exitErr.ExitCode() + if status == 2 { + return unix.ENOSPC + } + } + errorOut := fmt.Errorf("unpacking failed (error: %w; output: %s)", err, output) // when `xz -d -c -q | storage-untar ...` failed on storage-untar side, // we need to exhaust `xz`'s output, otherwise the `xz` side will be