134 lines
3.3 KiB
Go
134 lines
3.3 KiB
Go
package tim
|
|
|
|
import (
|
|
"archive/tar"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
ExecCommand = exec.Command
|
|
)
|
|
|
|
func Run(timPath string) error {
|
|
// Create a temporary directory to unpack the tim file.
|
|
tempDir, err := os.MkdirTemp("", "borg-run-*")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create temporary directory: %w", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Unpack the tim file.
|
|
file, err := os.Open(timPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open tim file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
tr := tar.NewReader(file)
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
target := filepath.Join(tempDir, hdr.Name)
|
|
target = filepath.Clean(target)
|
|
if !strings.HasPrefix(target, filepath.Clean(tempDir)+string(os.PathSeparator)) && target != filepath.Clean(tempDir) {
|
|
return fmt.Errorf("invalid file path: %s", hdr.Name)
|
|
}
|
|
|
|
switch hdr.Typeflag {
|
|
case tar.TypeDir:
|
|
if err := os.MkdirAll(target, 0755); err != nil {
|
|
return fmt.Errorf("failed to create directory: %w", err)
|
|
}
|
|
case tar.TypeReg:
|
|
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
|
return fmt.Errorf("failed to create directory: %w", err)
|
|
}
|
|
outFile, err := os.Create(target)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create file: %w", err)
|
|
}
|
|
if _, err := io.Copy(outFile, tr); err != nil {
|
|
outFile.Close()
|
|
return fmt.Errorf("failed to write file: %w", err)
|
|
}
|
|
if err := outFile.Close(); err != nil {
|
|
return fmt.Errorf("failed to close file: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run the tim.
|
|
cmd := ExecCommand("runc", "run", "-b", tempDir, "borg-container")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
return cmd.Run()
|
|
}
|
|
|
|
// RunEncrypted runs an encrypted .stim file.
|
|
// It decrypts the file, extracts the contents to a temporary directory,
|
|
// and runs the container using runc.
|
|
func RunEncrypted(stimPath, password string) error {
|
|
// Read the encrypted file
|
|
data, err := os.ReadFile(stimPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read stim file: %w", err)
|
|
}
|
|
|
|
// Decrypt and parse
|
|
m, err := FromSigil(data, password)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decrypt stim: %w", err)
|
|
}
|
|
|
|
// Create temp directory
|
|
tempDir, err := os.MkdirTemp("", "borg-run-*")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create temporary directory: %w", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Write config.json
|
|
configPath := filepath.Join(tempDir, "config.json")
|
|
if err := os.WriteFile(configPath, m.Config, 0600); err != nil {
|
|
return fmt.Errorf("failed to write config: %w", err)
|
|
}
|
|
|
|
// Extract rootfs
|
|
rootfsPath := filepath.Join(tempDir, "rootfs")
|
|
if err := os.MkdirAll(rootfsPath, 0755); err != nil {
|
|
return fmt.Errorf("failed to create rootfs dir: %w", err)
|
|
}
|
|
|
|
// Walk DataNode and extract files
|
|
err = m.RootFS.Walk(".", func(path string, d fs.DirEntry, walkErr error) error {
|
|
if walkErr != nil {
|
|
return walkErr
|
|
}
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
target := filepath.Join(rootfsPath, path)
|
|
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
|
return err
|
|
}
|
|
return m.RootFS.CopyFile(path, target, 0600)
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extract rootfs: %w", err)
|
|
}
|
|
|
|
// Run with runc
|
|
cmd := ExecCommand("runc", "run", "-b", tempDir, "borg-container")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
return cmd.Run()
|
|
}
|