Skip to content

Commit

Permalink
go/runtime/bundle: Explode bundle into an arbitrary directory
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Jan 31, 2025
1 parent 00b64bb commit 3af4e6f
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 78 deletions.
Empty file added .changelog/6029.trivial.md
Empty file.
116 changes: 52 additions & 64 deletions go/runtime/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions go/runtime/bundle/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
})
}
Expand Down Expand Up @@ -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)")
})
}
Expand Down
4 changes: 2 additions & 2 deletions go/runtime/bundle/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
6 changes: 2 additions & 4 deletions go/runtime/host/sandbox/provisioner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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) {
Expand Down
6 changes: 2 additions & 4 deletions go/runtime/host/sgx/provisioner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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) {
Expand Down

0 comments on commit 3af4e6f

Please sign in to comment.