mirror of https://github.com/containers/podman.git
Merge pull request #21977 from mtrmac/close-error-handling
Close error handling
This commit is contained in:
commit
49f290685f
|
@ -1,7 +1,6 @@
|
||||||
package compression
|
package compression
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -25,7 +24,7 @@ type decompressor interface {
|
||||||
compressedFileSize() int64
|
compressedFileSize() int64
|
||||||
compressedFileMode() os.FileMode
|
compressedFileMode() os.FileMode
|
||||||
compressedFileReader() (io.ReadCloser, error)
|
compressedFileReader() (io.ReadCloser, error)
|
||||||
decompress(w WriteSeekCloser, r io.Reader) error
|
decompress(w io.WriteSeeker, r io.Reader) error
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +71,7 @@ func newDecompressor(compressedFilePath string, compressedFileContent []byte) (d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDecompression(d decompressor, decompressedFilePath string) error {
|
func runDecompression(d decompressor, decompressedFilePath string) (retErr error) {
|
||||||
compressedFileReader, err := d.compressedFileReader()
|
compressedFileReader, err := d.compressedFileReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -99,8 +98,11 @@ func runDecompression(d decompressor, decompressedFilePath string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := decompressedFileWriter.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
|
if err := decompressedFileWriter.Close(); err != nil {
|
||||||
logrus.Warnf("Unable to to close destination file %s: %q", decompressedFilePath, err)
|
logrus.Warnf("Unable to to close destination file %s: %q", decompressedFilePath, err)
|
||||||
|
if retErr == nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (d *genericDecompressor) compressedFileReader() (io.ReadCloser, error) {
|
||||||
return compressedFile, nil
|
return compressedFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *genericDecompressor) decompress(w WriteSeekCloser, r io.Reader) error {
|
func (d *genericDecompressor) decompress(w io.WriteSeeker, r io.Reader) error {
|
||||||
decompressedFileReader, _, err := compression.AutoDecompress(r)
|
decompressedFileReader, _, err := compression.AutoDecompress(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -64,7 +64,7 @@ func (d *genericDecompressor) close() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *genericDecompressor) sparseOptimizedCopy(w WriteSeekCloser, r io.Reader) error {
|
func (d *genericDecompressor) sparseOptimizedCopy(w io.WriteSeeker, r io.Reader) error {
|
||||||
var err error
|
var err error
|
||||||
sparseWriter := NewSparseWriter(w)
|
sparseWriter := NewSparseWriter(w)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
|
@ -16,7 +16,7 @@ func newGzipDecompressor(compressedFilePath string) (*gzipDecompressor, error) {
|
||||||
return &gzipDecompressor{*d}, err
|
return &gzipDecompressor{*d}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *gzipDecompressor) decompress(w WriteSeekCloser, r io.Reader) error {
|
func (d *gzipDecompressor) decompress(w io.WriteSeeker, r io.Reader) error {
|
||||||
gzReader, err := image.GzipDecompressor(r)
|
gzReader, err := image.GzipDecompressor(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -14,14 +14,18 @@ type WriteSeekCloser interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type sparseWriter struct {
|
type sparseWriter struct {
|
||||||
file WriteSeekCloser
|
file io.WriteSeeker
|
||||||
// Invariant between method calls:
|
// Invariant between method calls:
|
||||||
// The contents of the file match the contents passed to Write, except that pendingZeroes trailing zeroes have not been written.
|
// The contents of the file match the contents passed to Write, except that pendingZeroes trailing zeroes have not been written.
|
||||||
// Also, the data that _has_ been written does not end with a zero byte (i.e. pendingZeroes is the largest possible value.
|
// Also, the data that _has_ been written does not end with a zero byte (i.e. pendingZeroes is the largest possible value.
|
||||||
pendingZeroes int64
|
pendingZeroes int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSparseWriter(file WriteSeekCloser) *sparseWriter {
|
// NewSparseWriter returns a WriteCloser for underlying file which creates
|
||||||
|
// holes where appropriate.
|
||||||
|
// NOTE: The caller must .Close() both the returned sparseWriter AND the underlying file,
|
||||||
|
// in that order.
|
||||||
|
func NewSparseWriter(file io.WriteSeeker) *sparseWriter {
|
||||||
return &sparseWriter{
|
return &sparseWriter{
|
||||||
file: file,
|
file: file,
|
||||||
pendingZeroes: 0,
|
pendingZeroes: 0,
|
||||||
|
@ -121,18 +125,15 @@ func (sw *sparseWriter) Close() error {
|
||||||
if sw.pendingZeroes != 0 {
|
if sw.pendingZeroes != 0 {
|
||||||
if holeSize := sw.pendingZeroes - 1; holeSize >= zerosThreshold {
|
if holeSize := sw.pendingZeroes - 1; holeSize >= zerosThreshold {
|
||||||
if err := sw.createHole(holeSize); err != nil {
|
if err := sw.createHole(holeSize); err != nil {
|
||||||
sw.file.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sw.pendingZeroes -= holeSize
|
sw.pendingZeroes -= holeSize
|
||||||
}
|
}
|
||||||
var zeroArray [zerosThreshold]byte
|
var zeroArray [zerosThreshold]byte
|
||||||
if _, err := sw.file.Write(zeroArray[:sw.pendingZeroes]); err != nil {
|
if _, err := sw.file.Write(zeroArray[:sw.pendingZeroes]); err != nil {
|
||||||
sw.file.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := sw.file.Close()
|
|
||||||
sw.file = nil
|
sw.file = nil
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,10 +58,6 @@ func (m *memorySparseFile) Write(b []byte) (n int, err error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memorySparseFile) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func testInputWithWriteLen(t *testing.T, input []byte, minSparse int64, chunkSize int) {
|
func testInputWithWriteLen(t *testing.T, input []byte, minSparse int64, chunkSize int) {
|
||||||
m := &memorySparseFile{}
|
m := &memorySparseFile{}
|
||||||
sparseWriter := NewSparseWriter(m)
|
sparseWriter := NewSparseWriter(m)
|
||||||
|
|
|
@ -13,6 +13,6 @@ func newUncompressedDecompressor(compressedFilePath string) (*uncompressedDecomp
|
||||||
return &uncompressedDecompressor{*d}, err
|
return &uncompressedDecompressor{*d}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *uncompressedDecompressor) decompress(w WriteSeekCloser, r io.Reader) error {
|
func (d *uncompressedDecompressor) decompress(w io.WriteSeeker, r io.Reader) error {
|
||||||
return d.sparseOptimizedCopy(w, r)
|
return d.sparseOptimizedCopy(w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func newXzDecompressor(compressedFilePath string) (*xzDecompressor, error) {
|
||||||
// Will error out if file without .Xz already exists
|
// Will error out if file without .Xz already exists
|
||||||
// Maybe extracting then renaming is a good idea here..
|
// Maybe extracting then renaming is a good idea here..
|
||||||
// depends on Xz: not pre-installed on mac, so it becomes a brew dependency
|
// depends on Xz: not pre-installed on mac, so it becomes a brew dependency
|
||||||
func (*xzDecompressor) decompress(w WriteSeekCloser, r io.Reader) error {
|
func (*xzDecompressor) decompress(w io.WriteSeeker, r io.Reader) error {
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
var read io.Reader
|
var read io.Reader
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (d *zipDecompressor) compressedFileReader() (io.ReadCloser, error) {
|
||||||
return z, nil
|
return z, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*zipDecompressor) decompress(w WriteSeekCloser, r io.Reader) error {
|
func (*zipDecompressor) decompress(w io.WriteSeeker, r io.Reader) error {
|
||||||
_, err := io.Copy(w, r)
|
_, err := io.Copy(w, r)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ func newZstdDecompressor(compressedFilePath string) (*zstdDecompressor, error) {
|
||||||
return &zstdDecompressor{*d}, err
|
return &zstdDecompressor{*d}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *zstdDecompressor) decompress(w WriteSeekCloser, r io.Reader) error {
|
func (d *zstdDecompressor) decompress(w io.WriteSeeker, r io.Reader) error {
|
||||||
zstdReader, err := zstd.NewReader(r)
|
zstdReader, err := zstd.NewReader(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package e2e_test
|
package e2e_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -121,9 +119,8 @@ func setup() (string, *machineTestBuilder) {
|
||||||
Fail(fmt.Sprintf("failed to create file %s: %q", mb.imagePath, err))
|
Fail(fmt.Sprintf("failed to create file %s: %q", mb.imagePath, err))
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
closeErr := dest.Close()
|
if err := dest.Close(); err != nil {
|
||||||
if err != nil || !errors.Is(closeErr, fs.ErrClosed) {
|
Fail(fmt.Sprintf("failed to close destination file %q: %q\n", dest.Name(), err))
|
||||||
fmt.Printf("failed to close destination file %q: %q\n", dest.Name(), err)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
fmt.Printf("--> copying %q to %q\n", src.Name(), dest.Name())
|
fmt.Printf("--> copying %q to %q\n", src.Name(), dest.Name())
|
||||||
|
@ -161,26 +158,14 @@ func teardown(origHomeDir string, testDir string, mb *machineTestBuilder) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copySparseFile is a helper method for tests only. copies a file sparsely
|
|
||||||
// between two string inputs
|
|
||||||
func copySparseFile(src, dst string) error { //nolint:unused
|
|
||||||
dstWriter, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dstWriter.Close()
|
|
||||||
fSrc, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fSrc.Close()
|
|
||||||
return copySparse(dstWriter, fSrc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// copySparse is a helper method for tests only; caller is responsible for closures
|
// copySparse is a helper method for tests only; caller is responsible for closures
|
||||||
func copySparse(dst compression.WriteSeekCloser, src io.Reader) error {
|
func copySparse(dst io.WriteSeeker, src io.Reader) (retErr error) {
|
||||||
spWriter := compression.NewSparseWriter(dst)
|
spWriter := compression.NewSparseWriter(dst)
|
||||||
defer spWriter.Close()
|
defer func() {
|
||||||
|
if err := spWriter.Close(); err != nil && retErr == nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
}()
|
||||||
_, err := io.Copy(spWriter, src)
|
_, err := io.Copy(spWriter, src)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue