diff --git a/.changelog/6029.trivial.md b/.changelog/6029.trivial.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/runtime/bundle/bundle.go b/go/runtime/bundle/bundle.go index d1c316be2f9..13ab58a0578 100644 --- a/go/runtime/bundle/bundle.go +++ b/go/runtime/bundle/bundle.go @@ -401,100 +401,88 @@ func ExplodedPath(dataDir string) string { return filepath.Join(dataDir, "runtimes", "bundles") } -// ExplodedPath returns the path that the corresponding asset will be written to via WriteExploded. -func (bnd *Bundle) ExplodedPath(dataDir, fn string) string { - return filepath.Join(ExplodedPath(dataDir), bnd.manifestHash.String(), fn) +// ExplodedPath returns the path under the data directory that contains the exploded bundle assets. +func (bnd *Bundle) ExplodedPath(dataDir string) string { + return filepath.Join(ExplodedPath(dataDir), bnd.manifestHash.String()) } -// WriteExploded extracts the runtime bundle, writes it to the appropriate -// data directory, and returns the path to the written location. -func (bnd *Bundle) WriteExploded(dataDir string) (string, error) { +// WriteExploded extracts the runtime bundle to the given directory. +func (bnd *Bundle) WriteExploded(dir string) error { if err := bnd.Validate(); err != nil { - return "", fmt.Errorf("runtime/bundle: refusing to explode malformed bundle: %w", err) - } - - subDir := bnd.ExplodedPath(dataDir, "") - - // Check to see if we have done this before, and be nice to SSDs by - // just verifying extracted data for correctness. - switch _, err := os.Stat(subDir); err { - case nil: - // Validate that the on-disk assets match the bundle contents. - // - // Note: This ignores extra garbage that may be on disk, but - // people that mess with internal directories get what they - // deserve. - for fn, expected := range bnd.Data { - fn = bnd.ExplodedPath(dataDir, fn) - h, rdErr := HashAllData(NewFileData(fn)) - if rdErr != nil { - return "", fmt.Errorf("runtime/bundle: failed to re-load asset '%s': %w", fn, rdErr) - } - - he, rdErr := HashAllData(expected) - if rdErr != nil { - return "", fmt.Errorf("runtime/bundle: failed to re-load asset '%s': %w", fn, rdErr) - } + return fmt.Errorf("runtime/bundle: refusing to explode malformed bundle: %w", err) + } - if !h.Equal(&he) { - return "", fmt.Errorf("runtime/bundle: corrupt asset: '%s'", fn) - } - } - default: - // Extract the bundle to disk. - if !os.IsNotExist(err) { - return "", fmt.Errorf("runtime/bundle: failed to stat asset directory '%s': %w", subDir, err) + // Create the required directory structure. + for _, path := range []string{ + dir, + filepath.Join(dir, manifestPath), + } { + if err := os.MkdirAll(path, 0o700); err != nil { + return fmt.Errorf("runtime/bundle: failed to create directory '%s': %w", path, err) } + } - for _, v := range []string{ - subDir, - bnd.ExplodedPath(dataDir, manifestPath), - } { - if err = os.MkdirAll(v, 0o700); err != nil { - return "", fmt.Errorf("runtime/bundle: failed to create asset sub-dir '%s': %w", v, err) - } - } - for fn, data := range bnd.Data { - fn = bnd.ExplodedPath(dataDir, fn) + // Extract the bundle to disk. + for fn, data := range bnd.Data { + path := filepath.Join(dir, fn) + // Check to see if we have done this before, and be nice to SSDs by + // just verifying extracted data for correctness. + switch _, err := os.Stat(path); err { + case nil: + // Validate that the on-disk asset matches the bundle contents. + h, err := HashAllData(NewFileData(path)) + if err != nil { + return fmt.Errorf("runtime/bundle: failed to hash asset '%s': %w", path, err) + } + he, err := HashAllData(data) + if err != nil { + return fmt.Errorf("runtime/bundle: failed to hash data '%s': %w", path, err) + } + if !h.Equal(&he) { + return fmt.Errorf("runtime/bundle: corrupted asset: '%s'", path) + } + default: + // Extract the asset to disk. err = func() error { var src io.ReadCloser if src, err = data.Open(); err != nil { - return fmt.Errorf("runtime/bundle: failed to open source asset '%s': %w", fn, err) + return fmt.Errorf("runtime/bundle: failed to open source asset '%s': %w", path, err) } defer src.Close() var f *os.File - if f, err = os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600); err != nil { - return fmt.Errorf("runtime/bundle: failed to write asset '%s': %w", fn, err) + if f, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600); err != nil { + return fmt.Errorf("runtime/bundle: failed to write asset '%s': %w", path, err) } defer f.Close() if _, err = io.Copy(f, src); err != nil { - return fmt.Errorf("runtime/bundle: failed to write asset '%s': %w", fn, err) + return fmt.Errorf("runtime/bundle: failed to write asset '%s': %w", path, err) } return nil }() if err != nil { - return "", err + return err } } + } - for id, comp := range bnd.Manifest.GetAvailableComponents() { - if comp.ELF != nil { - if err := os.Chmod(bnd.ExplodedPath(dataDir, comp.ELF.Executable), 0o700); err != nil { - return "", fmt.Errorf("runtime/bundle: failed to fixup executable permissions for '%s': %w", id, err) - } - } else if comp.Executable != "" { - if err := os.Chmod(bnd.ExplodedPath(dataDir, comp.Executable), 0o700); err != nil { - return "", fmt.Errorf("runtime/bundle: failed to fixup executable permissions for '%s': %w", id, err) - } + // Fix executable permissions. + for id, comp := range bnd.Manifest.GetAvailableComponents() { + if comp.ELF != nil { + if err := os.Chmod(filepath.Join(dir, comp.ELF.Executable), 0o700); err != nil { + return fmt.Errorf("runtime/bundle: failed to fixup executable permissions for '%s': %w", id, err) + } + } else if comp.Executable != "" { + if err := os.Chmod(filepath.Join(dir, comp.Executable), 0o700); err != nil { + return fmt.Errorf("runtime/bundle: failed to fixup executable permissions for '%s': %w", id, err) } } } - return subDir, nil + return nil } // Close closes the bundle, releasing resources. diff --git a/go/runtime/bundle/bundle_test.go b/go/runtime/bundle/bundle_test.go index 284d9c4074f..f578ee99a82 100644 --- a/go/runtime/bundle/bundle_test.go +++ b/go/runtime/bundle/bundle_test.go @@ -111,12 +111,12 @@ func TestBundle(t *testing.T) { }) t.Run("Explode", func(t *testing.T) { - _, err := bundle.WriteExploded(tmpDir) + err := bundle.WriteExploded(tmpDir) require.NoError(t, err, "WriteExploded") // Abuse the fact that we do an integrity check if the bundle // is already exploded. - _, err = bundle.WriteExploded(tmpDir) + err = bundle.WriteExploded(tmpDir) require.NoError(t, err, "WriteExploded(again)") }) } @@ -179,12 +179,12 @@ func TestDetachedBundle(t *testing.T) { }) t.Run("Explode", func(t *testing.T) { - _, err := bundle.WriteExploded(tmpDir) + err := bundle.WriteExploded(tmpDir) require.NoError(t, err, "WriteExploded") // Abuse the fact that we do an integrity check if the bundle // is already exploded. - _, err = bundle.WriteExploded(tmpDir) + err = bundle.WriteExploded(tmpDir) require.NoError(t, err, "WriteExploded(again)") }) } diff --git a/go/runtime/bundle/manager.go b/go/runtime/bundle/manager.go index a9b8635fd68..c64378a1ee2 100644 --- a/go/runtime/bundle/manager.go +++ b/go/runtime/bundle/manager.go @@ -674,8 +674,8 @@ func (m *Manager) explodeBundle(path string, opts ...OpenOption) (*ExplodedManif } defer bnd.Close() - dir, err := bnd.WriteExploded(m.dataDir) - if err != nil { + dir := bnd.ExplodedPath(m.dataDir) + if err = bnd.WriteExploded(dir); err != nil { return nil, fmt.Errorf("failed to explode bundle: %w", err) } diff --git a/go/runtime/host/sandbox/provisioner_test.go b/go/runtime/host/sandbox/provisioner_test.go index cc0860906a6..bc61910469e 100644 --- a/go/runtime/host/sandbox/provisioner_test.go +++ b/go/runtime/host/sandbox/provisioner_test.go @@ -29,11 +29,9 @@ func TestProvisionerSandbox(t *testing.T) { require.NoError(t, err, "bundle.Open") tmpDir := t.TempDir() - _, err = bnd.WriteExploded(tmpDir) + err = bnd.WriteExploded(tmpDir) require.NoError(t, err, "bnd.WriteExploded") - explodedDataDir := bnd.ExplodedPath(tmpDir, "") - cfg := host.Config{ Name: bnd.Manifest.Name, ID: bnd.Manifest.ID, @@ -42,7 +40,7 @@ func TestProvisionerSandbox(t *testing.T) { for _, comp := range bnd.Manifest.Components { cfg.Component = &bundle.ExplodedComponent{ Component: comp, - ExplodedDataDir: explodedDataDir, + ExplodedDataDir: tmpDir, } t.Run(fmt.Sprintf("Naked %s", comp.ID()), func(t *testing.T) { diff --git a/go/runtime/host/sgx/provisioner_test.go b/go/runtime/host/sgx/provisioner_test.go index 6fd8fa9f110..a635915a547 100644 --- a/go/runtime/host/sgx/provisioner_test.go +++ b/go/runtime/host/sgx/provisioner_test.go @@ -52,11 +52,9 @@ func TestProvisionerSGX(t *testing.T) { require.NoError(err, "bundle.Open") tmpDir := t.TempDir() - _, err = bnd.WriteExploded(tmpDir) + err = bnd.WriteExploded(tmpDir) require.NoError(err, "bnd.WriteExploded") - explodedDataDir := bnd.ExplodedPath(tmpDir, "") - ias, err := iasHttp.New(&iasHttp.Config{ SPID: "9b3085a55a5863f7cc66b380dcad0082", QuoteSignatureType: cmnIAS.SignatureUnlinkable, @@ -79,7 +77,7 @@ func TestProvisionerSGX(t *testing.T) { for _, comp := range bnd.Manifest.Components { cfg.Component = &bundle.ExplodedComponent{ Component: comp, - ExplodedDataDir: explodedDataDir, + ExplodedDataDir: tmpDir, } t.Run(fmt.Sprintf("Naked %s", comp.ID()), func(t *testing.T) {