From 61b9129e0c29b2402235065f27888459e6054e7a Mon Sep 17 00:00:00 2001 From: Noel Georgi Date: Thu, 21 Nov 2024 14:24:52 +0530 Subject: [PATCH] fix: add directory entries and filemode to tarball Always add directory entries to tarball. Handle directory v/s files in tarball `build`. Use the source file info to set file permissions. Signed-off-by: Noel Georgi --- pkg/imager/cache/cache.go | 4 -- pkg/imager/filemap/filemap.go | 66 +++++++++++++++++++++++------- pkg/imager/filemap/filemap_test.go | 65 +++++++++++++++++++++++++++++ pkg/imager/out.go | 6 --- 4 files changed, 117 insertions(+), 24 deletions(-) create mode 100644 pkg/imager/filemap/filemap_test.go diff --git a/pkg/imager/cache/cache.go b/pkg/imager/cache/cache.go index 65b807a7fe..79488d5559 100644 --- a/pkg/imager/cache/cache.go +++ b/pkg/imager/cache/cache.go @@ -196,10 +196,6 @@ func Generate(images []string, platform string, insecure bool, dest string) erro return fmt.Errorf("walking filesystem: %w", err) } - for i := range artifacts { - artifacts[i].ImageMode = 0o644 - } - artifactsLayer, err := filemap.Layer(artifacts) if err != nil { return fmt.Errorf("creating artifacts layer: %w", err) diff --git a/pkg/imager/filemap/filemap.go b/pkg/imager/filemap/filemap.go index 2326c55695..8943d39aaf 100644 --- a/pkg/imager/filemap/filemap.go +++ b/pkg/imager/filemap/filemap.go @@ -8,6 +8,7 @@ package filemap import ( "archive/tar" "cmp" + "fmt" "io" "os" "path/filepath" @@ -33,11 +34,16 @@ func Walk(sourceBasePath, imageBasePath string) ([]File, error) { return err } - if d.IsDir() { + rel, err := filepath.Rel(sourceBasePath, path) + if err != nil { + return err + } + + if d.IsDir() && rel == "." { return nil } - rel, err := filepath.Rel(sourceBasePath, path) + statInfo, err := d.Info() if err != nil { return err } @@ -45,6 +51,7 @@ func Walk(sourceBasePath, imageBasePath string) ([]File, error) { filemap = append(filemap, File{ ImagePath: filepath.Join(imageBasePath, rel), SourcePath: path, + ImageMode: int64(statInfo.Mode().Perm()), }) return nil @@ -64,32 +71,31 @@ func build(filemap []File) io.ReadCloser { if err := func(entry File) error { in, err := os.Open(entry.SourcePath) if err != nil { - return err + return fmt.Errorf("error opening %q: %w", entry.SourcePath, err) } defer in.Close() //nolint:errcheck st, err := in.Stat() if err != nil { - return err + return fmt.Errorf("error stating file %s: %w", entry.SourcePath, err) } - if err = w.WriteHeader(&tar.Header{ - Name: entry.ImagePath, - Size: st.Size(), - Mode: entry.ImageMode, - }); err != nil { - return err + if st.IsDir() { + if err := handleDir(w, entry.ImagePath, entry.ImageMode); err != nil { + return fmt.Errorf("error handling directory %s: %w", entry.SourcePath, err) + } + + return in.Close() } - _, err = io.Copy(w, in) - if err != nil { - return err + if err := handleFile(w, in, entry.ImagePath, entry.ImageMode, st.Size()); err != nil { + return fmt.Errorf("error handling file %s: %w", entry.SourcePath, err) } return in.Close() }(entry); err != nil { - return err + return fmt.Errorf("error processing %s: %w", entry.SourcePath, err) } } @@ -100,6 +106,38 @@ func build(filemap []File) io.ReadCloser { return pr } +func handleFile(w *tar.Writer, r io.Reader, path string, mode, size int64) error { + header := &tar.Header{ + Name: path, + Mode: mode, + Size: size, + } + + if err := w.WriteHeader(header); err != nil { + return fmt.Errorf("error writing tar header for %s: %w", path, err) + } + + if _, err := io.Copy(w, r); err != nil { + return fmt.Errorf("error writing tar data for %s: %w", path, err) + } + + return nil +} + +func handleDir(w *tar.Writer, path string, mode int64) error { + header := &tar.Header{ + Name: path, + Mode: mode, + Typeflag: tar.TypeDir, + } + + if err := w.WriteHeader(header); err != nil { + return fmt.Errorf("error writing tar header for %s: %w", path, err) + } + + return nil +} + // Layer creates a layer from a single file map. // // These layers are reproducible and consistent. diff --git a/pkg/imager/filemap/filemap_test.go b/pkg/imager/filemap/filemap_test.go new file mode 100644 index 0000000000..785da4e865 --- /dev/null +++ b/pkg/imager/filemap/filemap_test.go @@ -0,0 +1,65 @@ +// 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 filemap_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/siderolabs/talos/pkg/imager/filemap" +) + +func TestFileMap(t *testing.T) { + tempDir := t.TempDir() + + assert.NoError(t, os.MkdirAll(filepath.Join(tempDir, "foo/a/b"), 0o755)) + assert.NoError(t, os.MkdirAll(filepath.Join(tempDir, "foo/c"), 0o755)) + + assert.NoError(t, os.WriteFile(filepath.Join(tempDir, "foo/a/b/normal"), nil, 0o644)) + assert.NoError(t, os.WriteFile(filepath.Join(tempDir, "foo/c/executable"), []byte("world"), 0o755)) + + artifacts, err := filemap.Walk(tempDir, "") + assert.NoError(t, err) + + assert.Equal( + t, + []filemap.File{ + { + ImagePath: "foo", + SourcePath: filepath.Join(tempDir, "foo"), + ImageMode: 0o755, + }, + { + ImagePath: "foo/a", + SourcePath: filepath.Join(tempDir, "foo/a"), + ImageMode: 0o755, + }, + { + ImagePath: "foo/a/b", + SourcePath: filepath.Join(tempDir, "foo/a/b"), + ImageMode: 0o755, + }, + { + ImagePath: "foo/a/b/normal", + SourcePath: filepath.Join(tempDir, "foo/a/b/normal"), + ImageMode: 0o644, + }, + { + ImagePath: "foo/c", + SourcePath: filepath.Join(tempDir, "foo/c"), + ImageMode: 0o755, + }, + { + ImagePath: "foo/c/executable", + SourcePath: filepath.Join(tempDir, "foo/c/executable"), + ImageMode: 0o755, + }, + }, + artifacts, + ) +} diff --git a/pkg/imager/out.go b/pkg/imager/out.go index 3dd32245eb..8080f0bf8a 100644 --- a/pkg/imager/out.go +++ b/pkg/imager/out.go @@ -463,7 +463,6 @@ func (i *Imager) outInstaller(ctx context.Context, path string, report *reporter for _, extraArtifact := range []struct { sourcePath string imagePath string - mode os.FileMode }{ { sourcePath: filepath.Join(i.tempDir, "overlay-installer", constants.ImagerOverlayArtifactsPath), @@ -472,7 +471,6 @@ func (i *Imager) outInstaller(ctx context.Context, path string, report *reporter { sourcePath: filepath.Join(i.tempDir, "overlay-installer", constants.ImagerOverlayInstallersPath, i.prof.Overlay.Name), imagePath: strings.TrimLeft(constants.ImagerOverlayInstallerDefaultPath, "/"), - mode: 0o755, }, { sourcePath: filepath.Join(i.tempDir, constants.ImagerOverlayExtraOptionsPath), @@ -486,10 +484,6 @@ func (i *Imager) outInstaller(ctx context.Context, path string, report *reporter return fmt.Errorf("failed to walk extra artifact %s: %w", extraArtifact.sourcePath, err) } - for i := range extraFiles { - extraFiles[i].ImageMode = int64(extraArtifact.mode) - } - overlayArtifacts = append(overlayArtifacts, extraFiles...) }