Merge pull request #2259 from aeijdenberg/setfixedtimestampforarchiveentries

feat: add Timestamp to TarOptions
This commit is contained in:
openshift-merge-bot[bot] 2025-02-20 20:30:11 +00:00 committed by GitHub
commit 3a013da40e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 2 deletions

View File

@ -16,6 +16,7 @@ import (
"strings"
"sync"
"syscall"
"time"
"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/idtools"
@ -67,6 +68,8 @@ type (
CopyPass bool
// ForceMask, if set, indicates the permission mask used for created files.
ForceMask *os.FileMode
// Timestamp, if set, will be set in each header as create/mod/access time
Timestamp *time.Time
}
)
@ -494,15 +497,19 @@ type tarWriter struct {
// from the traditional behavior/format to get features like subsecond
// precision in timestamps.
CopyPass bool
// Timestamp, if set, will be set in each header as create/mod/access time
Timestamp *time.Time
}
func newTarWriter(idMapping *idtools.IDMappings, writer io.Writer, chownOpts *idtools.IDPair) *tarWriter {
func newTarWriter(idMapping *idtools.IDMappings, writer io.Writer, chownOpts *idtools.IDPair, timestamp *time.Time) *tarWriter {
return &tarWriter{
SeenFiles: make(map[uint64]string),
TarWriter: tar.NewWriter(writer),
Buffer: pools.BufioWriter32KPool.Get(nil),
IDMappings: idMapping,
ChownOpts: chownOpts,
Timestamp: timestamp,
}
}
@ -600,6 +607,13 @@ func (ta *tarWriter) addFile(path, name string) error {
hdr.Gname = ""
}
// if override timestamp set, replace all times with this
if ta.Timestamp != nil {
hdr.ModTime = *ta.Timestamp
hdr.AccessTime = *ta.Timestamp
hdr.ChangeTime = *ta.Timestamp
}
maybeTruncateHeaderModTime(hdr)
if ta.WhiteoutConverter != nil {
@ -866,6 +880,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
compressWriter,
options.ChownOpts,
options.Timestamp,
)
ta.WhiteoutConverter = GetWhiteoutConverter(options.WhiteoutFormat, options.WhiteoutData)
ta.CopyPass = options.CopyPass

View File

@ -12,6 +12,7 @@ import (
"runtime"
"strings"
"testing"
"time"
"github.com/containers/storage/pkg/idtools"
"github.com/stretchr/testify/assert"
@ -1222,3 +1223,46 @@ func readFileFromArchive(t *testing.T, archive io.ReadCloser, name string, expec
assert.NoError(t, err)
return string(content)
}
func TestTimestamp(t *testing.T) {
// write single file into dir that we'll tar
td := t.TempDir()
tf := filepath.Join(td, "foo")
require.NoError(t, os.WriteFile(tf, []byte("bar"), 0o644))
// helper function to tar that dir and return byte slice
tarToByteSlice := func(options *TarOptions) []byte {
rc, err := TarWithOptions(td, options)
assert.NoError(t, err)
defer rc.Close()
rv, err := io.ReadAll(rc)
assert.NoError(t, err)
return rv
}
// default options
defaultOptions := &TarOptions{}
// override timestamp option
epochOptions := &TarOptions{Timestamp: &time.Time{}}
// get tar bytes slices now
origTarDefaultOptions := tarToByteSlice(defaultOptions)
origTarEpochOptions := tarToByteSlice(epochOptions)
// set the mod time of the file to an hour later
oneHourLater := time.Now().Add(time.Hour)
require.NoError(t, os.Chtimes(tf, oneHourLater, oneHourLater))
// then tar again
laterTarDefaultOptions := tarToByteSlice(defaultOptions)
laterTarEpochOptions := tarToByteSlice(epochOptions)
// we expect the ones without a fixed timestamp to be different
assert.NotEqual(t, origTarDefaultOptions, laterTarDefaultOptions)
// we expect the ones with a fixed timestamp to be the same
assert.Equal(t, origTarEpochOptions, laterTarEpochOptions)
}

View File

@ -452,7 +452,7 @@ func ChangesSize(newDir string, changes []Change) int64 {
func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMap) (io.ReadCloser, error) {
reader, writer := io.Pipe()
go func() {
ta := newTarWriter(idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), writer, nil)
ta := newTarWriter(idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), writer, nil, nil)
// this buffer is needed for the duration of this piped stream
defer pools.BufioWriter32KPool.Put(ta.Buffer)