mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-22 23:21:11 +02:00
This unifies low-level filesystem walker code for `ls` and `cp`. New features: * `ls` now reports relative filenames * `ls` now prints symlink destination for symlinks * `cp` now properly always reports errors from the API * `cp` now reports all the errors back to the client Example for `ls`: ``` osctl-linux-amd64 --talosconfig talosconfig ls -l /var MODE SIZE(B) LASTMOD NAME drwxr-xr-x 4096 Jun 26 2019 . Lrwxrwxrwx 4 Jun 25 2019 etc -> /etc drwxr-xr-x 4096 Jun 26 2019 lib drwxr-xr-x 4096 Jun 21 2019 libexec drwxr-xr-x 4096 Jun 26 2019 log drwxr-xr-x 4096 Jun 21 2019 mail drwxr-xr-x 4096 Jun 26 2019 opt Lrwxrwxrwx 6 Jun 21 2019 run -> ../run drwxr-xr-x 4096 Jun 21 2019 spool dtrwxrwxrwx 4096 Jun 21 2019 tmp -rw------- 14979 Jun 26 2019 userdata.yaml ``` Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
129 lines
2.6 KiB
Go
129 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()
|
|
}
|