Skip to content

Commit

Permalink
fix: add directory entries and filemode to tarball
Browse files Browse the repository at this point in the history
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 <git@frezbo.dev>
  • Loading branch information
frezbo committed Nov 21, 2024
1 parent 4caeae2 commit 61b9129
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 24 deletions.
4 changes: 0 additions & 4 deletions pkg/imager/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
66 changes: 52 additions & 14 deletions pkg/imager/filemap/filemap.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package filemap
import (
"archive/tar"
"cmp"
"fmt"
"io"
"os"
"path/filepath"
Expand All @@ -33,18 +34,24 @@ 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
}

filemap = append(filemap, File{
ImagePath: filepath.Join(imageBasePath, rel),
SourcePath: path,
ImageMode: int64(statInfo.Mode().Perm()),
})

return nil
Expand All @@ -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)
}
}

Expand All @@ -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.
Expand Down
65 changes: 65 additions & 0 deletions pkg/imager/filemap/filemap_test.go
Original file line number Diff line number Diff line change
@@ -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,
)
}
6 changes: 0 additions & 6 deletions pkg/imager/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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),
Expand All @@ -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...)
}

Expand Down

0 comments on commit 61b9129

Please sign in to comment.