talos/pkg/archiver/tar.go
Andrey Smirnov 8560fb9662 chore: enable nlreturn linter
Most of the fixes were automatically applied.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
2020-11-09 06:48:07 -08:00

137 lines
2.6 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package archiver
import (
"archive/tar"
"context"
"fmt"
"io"
"log"
"os"
multierror "github.com/hashicorp/go-multierror"
)
// Tar creates .tar archive and writes it to output for every item in paths channel
//
//nolint: gocyclo
func Tar(ctx context.Context, paths <-chan FileItem, output io.Writer) error {
tw := tar.NewWriter(output)
//nolint: errcheck
defer tw.Close()
var multiErr *multierror.Error
for fi := range paths {
if fi.Error != nil {
multiErr = multierror.Append(multiErr, fmt.Errorf("skipping %q: %s", fi.FullPath, fi.Error))
continue
}
header, err := tar.FileInfoHeader(fi.FileInfo, fi.Link)
if err != nil {
// not supported by tar
multiErr = multierror.Append(multiErr, fmt.Errorf("skipping %q: %s", fi.FullPath, err))
continue
}
header.Name = fi.RelPath
if fi.FileInfo.IsDir() {
header.Name += string(os.PathSeparator)
}
skipData := false
switch header.Typeflag {
case tar.TypeLink, tar.TypeSymlink, tar.TypeChar, tar.TypeBlock, tar.TypeDir, tar.TypeFifo:
// no data for these types, move on
skipData = true
}
if header.Size == 0 {
// skip files with zero length
//
// this might skip contents for special files in /proc, but
// anyways we can't archive them properly if we don't know size beforehand
skipData = true
}
var fp *os.File
if !skipData {
fp, err = os.Open(fi.FullPath)
if err != nil {
multiErr = multierror.Append(multiErr, fmt.Errorf("skipping %q: %s", fi.FullPath, err))
continue
}
}
err = tw.WriteHeader(header)
if err != nil {
//nolint: errcheck
fp.Close()
multiErr = multierror.Append(multiErr, err)
return multiErr
}
if !skipData {
err = archiveFile(ctx, tw, fi, fp)
if err != nil {
multiErr = multierror.Append(multiErr, err)
return multiErr
}
}
}
if err := tw.Close(); err != nil {
multiErr = multierror.Append(multiErr, err)
}
return multiErr.ErrorOrNil()
}
func archiveFile(ctx context.Context, tw io.Writer, fi FileItem, fp *os.File) error {
//nolint: errcheck
defer fp.Close()
buf := make([]byte, 4096)
for {
n, err := fp.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
select {
case <-ctx.Done():
return ctx.Err()
default:
}
_, err = tw.Write(buf[:n])
if err != nil {
if err == tar.ErrWriteTooLong {
log.Printf("ignoring long write for %q", fi.FullPath)
return nil
}
return err
}
}
return fp.Close()
}