Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions fileutil/compressor_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type Compressor interface {

DecompressFileToDir(path string, dir string, options CompressorOptions) (err error)

IsNonCompressedTarball(path string) (bool, error)

// CleanUp cleans up compressed file after it was used
CleanUp(path string) error
}
7 changes: 7 additions & 0 deletions fileutil/fakes/fake_compressor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type FakeCompressor struct {

CleanUpTarballPath string
CleanUpErr error

IsNonCompressedResult bool
IsNonCompressedErr error
}

func NewFakeCompressor() *FakeCompressor {
Expand Down Expand Up @@ -65,6 +68,10 @@ func (fc *FakeCompressor) DecompressFileToDir(tarballPath string, dir string, op
return fc.DecompressFileToDirErr
}

func (fc *FakeCompressor) IsNonCompressedTarball(path string) (bool, error) {
return fc.IsNonCompressedResult, fc.IsNonCompressedErr
}

func (fc *FakeCompressor) CleanUp(tarballPath string) error {
fc.CleanUpTarballPath = tarballPath
return fc.CleanUpErr
Expand Down
43 changes: 43 additions & 0 deletions fileutil/tarball_compressor.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package fileutil

import (
"bytes"
"fmt"
"os"
"runtime"

bosherr "github.com/cloudfoundry/bosh-utils/errors"
boshsys "github.com/cloudfoundry/bosh-utils/system"
)

var (
gzipMagic = []byte{0x1f, 0x8b}
bzip2Magic = []byte{0x42, 0x5a, 0x68} // "BZh"
xzMagic = []byte{0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00}
zstdMagic = []byte{0x28, 0xb5, 0x2f, 0xfd}
ustarMagic = []byte("ustar")
ustarOffset = 257 // Offset of the TAR magic string in the file
)

type tarballCompressor struct {
cmdRunner boshsys.CmdRunner
fs boshsys.FileSystem
Expand Down Expand Up @@ -80,6 +91,38 @@ func (c tarballCompressor) DecompressFileToDir(tarballPath string, dir string, o
return nil
}

func (c tarballCompressor) IsNonCompressedTarball(path string) (bool, error) {
f, err := c.fs.OpenFile(path, os.O_RDONLY, 0644)
if err != nil {
return false, fmt.Errorf("could not open file: %w", err)
}
defer f.Close()

// Read the first 512 bytes to check both compression headers and the TAR header.
// Ignore the error from reading a partial buffer, which is fine for short files.
buffer := make([]byte, 512)
_, _ = f.Read(buffer)

// 1. Check for compression first.
if bytes.HasPrefix(buffer, gzipMagic) ||
bytes.HasPrefix(buffer, bzip2Magic) ||
bytes.HasPrefix(buffer, xzMagic) ||
bytes.HasPrefix(buffer, zstdMagic) {
return false, nil
}

// 2. If NOT compressed, check for the TAR magic string at its specific offset.
// Ensure the buffer is long enough to contain the TAR header magic string.
if len(buffer) > ustarOffset+len(ustarMagic) {
magicBytes := buffer[ustarOffset : ustarOffset+len(ustarMagic)]
if bytes.Equal(magicBytes, ustarMagic) {
return true, nil
}
}

return false, nil
}

func (c tarballCompressor) CleanUp(tarballPath string) error {
return c.fs.RemoveAll(tarballPath)
}
82 changes: 82 additions & 0 deletions fileutil/tarball_compressor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,88 @@ var _ = Describe("tarballCompressor", func() {
})
})

Describe("IsNonCompressedTarball", func() {
It("returns true for non-compressed tarball created with NoCompression=true", func() {
tgzName, err := compressor.CompressFilesInDir(testAssetsFixtureDir, CompressorOptions{NoCompression: true})
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tgzName)

result, err := compressor.IsNonCompressedTarball(tgzName)
Expect(err).ToNot(HaveOccurred())
Expect(result).To(BeTrue())
})

It("returns false for compressed tarball created with NoCompression=false", func() {
tgzName, err := compressor.CompressFilesInDir(testAssetsFixtureDir, CompressorOptions{NoCompression: false})
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tgzName)

result, err := compressor.IsNonCompressedTarball(tgzName)
Expect(err).ToNot(HaveOccurred())
Expect(result).To(BeFalse())
})

It("returns false for compressed tarball created with default options", func() {
tgzName, err := compressor.CompressFilesInDir(testAssetsFixtureDir, CompressorOptions{})
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tgzName)

result, err := compressor.IsNonCompressedTarball(tgzName)
Expect(err).ToNot(HaveOccurred())
Expect(result).To(BeFalse())
})

It("returns error for non-existent file", func() {
result, err := compressor.IsNonCompressedTarball("/nonexistent/file.tar")
Expect(err).To(HaveOccurred())
Expect(result).To(BeFalse())
})

It("returns error for non-tarball file", func() {
tempFile, err := fs.TempFile("test-non-tarball")
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tempFile.Name())

err = fs.WriteFileString(tempFile.Name(), "This is not a tar file")
Expect(err).ToNot(HaveOccurred())

result, err := compressor.IsNonCompressedTarball(tempFile.Name())
Expect(err).ToNot(HaveOccurred())
Expect(result).To(BeFalse())
})

It("returns error for empty file", func() {
tempFile, err := fs.TempFile("test-empty-file")
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tempFile.Name())
tempFile.Close()

result, err := compressor.IsNonCompressedTarball(tempFile.Name())
Expect(err).ToNot(HaveOccurred())
Expect(result).To(BeFalse())
})

It("correctly identifies tarballs created with CompressSpecificFilesInDir", func() {
files := []string{"app.stdout.log", "app.stderr.log"}

tgzName, err := compressor.CompressSpecificFilesInDir(testAssetsFixtureDir, files, CompressorOptions{NoCompression: true})
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tgzName)

result, err := compressor.IsNonCompressedTarball(tgzName)
Expect(err).ToNot(HaveOccurred())
Expect(result).To(BeTrue())

tgzName2, err := compressor.CompressSpecificFilesInDir(testAssetsFixtureDir, files, CompressorOptions{NoCompression: false})
Expect(err).ToNot(HaveOccurred())
defer os.Remove(tgzName2)

result2, err := compressor.IsNonCompressedTarball(tgzName2)
Expect(err).ToNot(HaveOccurred())
Expect(result2).To(BeFalse())
})
})

Describe("CleanUp", func() {
It("removes tarball path", func() {
fs := fakesys.NewFakeFileSystem()
Expand Down
Loading