|
1 | 1 | package filex |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "fmt" |
5 | 6 | "github.com/mholt/archiver/v3" |
6 | 7 | "github.com/samber/lo" |
7 | 8 | "github.com/wttech/aemc/pkg/common/pathx" |
| 9 | + "io" |
8 | 10 | "os" |
9 | 11 | "path/filepath" |
10 | 12 | ) |
@@ -82,3 +84,47 @@ func UnarchiveWithChanged(sourceFile, targetDir string) (bool, error) { |
82 | 84 | } |
83 | 85 | return true, nil |
84 | 86 | } |
| 87 | + |
| 88 | +// UnarchiveMakeself extracts tar.gz archive from a Makeself self-extracting shell script. |
| 89 | +// Makeself (https://makeself.io/) creates self-extracting archives by embedding a tar.gz archive after a shell header. |
| 90 | +// This function finds the gzip magic bytes (0x1f 0x8b) and extracts everything after it. |
| 91 | +func UnarchiveMakeself(scriptPath string, targetDir string) error { |
| 92 | + file, err := os.Open(scriptPath) |
| 93 | + if err != nil { |
| 94 | + return fmt.Errorf("cannot open Makeself script file '%s': %w", scriptPath, err) |
| 95 | + } |
| 96 | + defer file.Close() |
| 97 | + |
| 98 | + // Read entire file to find embedded gzip archive |
| 99 | + data, err := io.ReadAll(file) |
| 100 | + if err != nil { |
| 101 | + return fmt.Errorf("cannot read Makeself script file '%s': %w", scriptPath, err) |
| 102 | + } |
| 103 | + |
| 104 | + // Find gzip magic bytes (1f 8b) which mark the start of embedded tar.gz archive |
| 105 | + gzipMagic := []byte{0x1f, 0x8b} |
| 106 | + archiveStart := bytes.Index(data, gzipMagic) |
| 107 | + if archiveStart == -1 { |
| 108 | + return fmt.Errorf("cannot find gzip archive in Makeself script file '%s' (missing gzip magic bytes 0x1f 0x8b)", scriptPath) |
| 109 | + } |
| 110 | + |
| 111 | + // Extract archive data to temporary file as sibling of the script file |
| 112 | + tmpFile, err := os.CreateTemp(filepath.Dir(scriptPath), filepath.Base(scriptPath)+"-*.tar.gz") |
| 113 | + if err != nil { |
| 114 | + return fmt.Errorf("cannot create temporary archive file in dir '%s' for Makeself extraction: %w", filepath.Dir(scriptPath), err) |
| 115 | + } |
| 116 | + tmpArchive := tmpFile.Name() |
| 117 | + tmpFile.Close() |
| 118 | + defer os.Remove(tmpArchive) |
| 119 | + |
| 120 | + if err := os.WriteFile(tmpArchive, data[archiveStart:], 0644); err != nil { |
| 121 | + return fmt.Errorf("cannot write extracted Makeself archive data to temporary file '%s': %w", tmpArchive, err) |
| 122 | + } |
| 123 | + |
| 124 | + // Unpack the tar.gz using standard archiver |
| 125 | + if err := Unarchive(tmpArchive, targetDir); err != nil { |
| 126 | + return fmt.Errorf("cannot unarchive extracted Makeself tar.gz '%s' to dir '%s': %w", tmpArchive, targetDir, err) |
| 127 | + } |
| 128 | + |
| 129 | + return nil |
| 130 | +} |
0 commit comments