mirror of https://github.com/containers/podman.git
118 lines
2.3 KiB
Go
118 lines
2.3 KiB
Go
package compression
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
// TODO vendor this in ... pkg/os directory is small and code should be negligible
|
|
/*
|
|
NOTE: copy.go and copy.test were lifted from github.com/crc-org/crc because
|
|
i was having trouble getting go to vendor it properly. all credit to them
|
|
*/
|
|
|
|
func copyFile(src, dst string, sparse bool) error {
|
|
in, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer in.Close()
|
|
|
|
out, err := os.Create(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
if sparse {
|
|
if _, err = CopySparse(out, in); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if _, err = io.Copy(out, in); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
fi, err := os.Stat(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = os.Chmod(dst, fi.Mode()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return out.Close()
|
|
}
|
|
|
|
func CopyFile(src, dst string) error {
|
|
return copyFile(src, dst, false)
|
|
}
|
|
|
|
func CopyFileSparse(src, dst string) error {
|
|
return copyFile(src, dst, true)
|
|
}
|
|
|
|
func CopySparse(dst io.WriteSeeker, src io.Reader) (int64, error) {
|
|
copyBuf := make([]byte, copyChunkSize)
|
|
sparseWriter := newSparseWriter(dst)
|
|
|
|
bytesWritten, err := io.CopyBuffer(sparseWriter, src, copyBuf)
|
|
if err != nil {
|
|
return bytesWritten, err
|
|
}
|
|
err = sparseWriter.Close()
|
|
return bytesWritten, err
|
|
}
|
|
|
|
type sparseWriter struct {
|
|
writer io.WriteSeeker
|
|
lastChunkSparse bool
|
|
}
|
|
|
|
func newSparseWriter(writer io.WriteSeeker) *sparseWriter {
|
|
return &sparseWriter{writer: writer}
|
|
}
|
|
|
|
const copyChunkSize = 4096
|
|
|
|
var emptyChunk = make([]byte, copyChunkSize)
|
|
|
|
func isEmptyChunk(p []byte) bool {
|
|
// HasPrefix instead of bytes.Equal in order to handle the last chunk
|
|
// of the file, which may be shorter than len(emptyChunk), and would
|
|
// fail bytes.Equal()
|
|
return bytes.HasPrefix(emptyChunk, p)
|
|
}
|
|
|
|
func (w *sparseWriter) Write(p []byte) (n int, err error) {
|
|
if isEmptyChunk(p) {
|
|
offset, err := w.writer.Seek(int64(len(p)), io.SeekCurrent)
|
|
if err != nil {
|
|
w.lastChunkSparse = false
|
|
return 0, err
|
|
}
|
|
_ = offset
|
|
w.lastChunkSparse = true
|
|
return len(p), nil
|
|
}
|
|
w.lastChunkSparse = false
|
|
return w.writer.Write(p)
|
|
}
|
|
|
|
func (w *sparseWriter) Close() error {
|
|
if w.lastChunkSparse {
|
|
if _, err := w.writer.Seek(-1, io.SeekCurrent); err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.writer.Write([]byte{0}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|