Skip to content

Commit e9b5feb

Browse files
authored
chore: extracting compressed files to correct location (#1817)
1 parent 0ac6c3b commit e9b5feb

File tree

1 file changed

+213
-9
lines changed

1 file changed

+213
-9
lines changed

Diff for: component/updater/update_ui.go

+213-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package updater
22

33
import (
4+
"archive/tar"
45
"archive/zip"
6+
"compress/gzip"
57
"fmt"
68
"io"
79
"os"
@@ -22,6 +24,14 @@ type UIUpdater struct {
2224
mutex sync.Mutex
2325
}
2426

27+
type compressionType int
28+
29+
const (
30+
typeUnknown compressionType = iota
31+
typeZip
32+
typeTarGzip
33+
)
34+
2535
var DefaultUiUpdater = &UIUpdater{}
2636

2737
func NewUiUpdater(externalUI, externalUIURL, externalUIName string) *UIUpdater {
@@ -70,6 +80,24 @@ func (u *UIUpdater) DownloadUI() error {
7080
return u.downloadUI()
7181
}
7282

83+
func detectFileType(data []byte) compressionType {
84+
if len(data) < 4 {
85+
return typeUnknown
86+
}
87+
88+
// Zip: 0x50 0x4B 0x03 0x04
89+
if data[0] == 0x50 && data[1] == 0x4B && data[2] == 0x03 && data[3] == 0x04 {
90+
return typeZip
91+
}
92+
93+
// GZip: 0x1F 0x8B
94+
if data[0] == 0x1F && data[1] == 0x8B {
95+
return typeTarGzip
96+
}
97+
98+
return typeUnknown
99+
}
100+
73101
func (u *UIUpdater) downloadUI() error {
74102
err := u.prepareUIPath()
75103
if err != nil {
@@ -78,12 +106,23 @@ func (u *UIUpdater) downloadUI() error {
78106

79107
data, err := downloadForBytes(u.externalUIURL)
80108
if err != nil {
81-
return fmt.Errorf("can't download file: %w", err)
109+
return fmt.Errorf("can't download file: %w", err)
82110
}
83111

84-
saved := path.Join(C.Path.HomeDir(), "download.zip")
112+
fileType := detectFileType(data)
113+
if fileType == typeUnknown {
114+
return fmt.Errorf("unknown or unsupported file type")
115+
}
116+
117+
ext := ".zip"
118+
if fileType == typeTarGzip {
119+
ext = ".tgz"
120+
}
121+
122+
saved := path.Join(C.Path.HomeDir(), "download"+ext)
123+
log.Debugln("compression Type: %s", ext)
85124
if err = saveFile(data, saved); err != nil {
86-
return fmt.Errorf("can't save zip file: %w", err)
125+
return fmt.Errorf("can't save compressed file: %w", err)
87126
}
88127
defer os.Remove(saved)
89128

@@ -94,12 +133,12 @@ func (u *UIUpdater) downloadUI() error {
94133
}
95134
}
96135

97-
unzipFolder, err := unzip(saved, C.Path.HomeDir())
136+
extractedFolder, err := extract(saved, C.Path.HomeDir())
98137
if err != nil {
99-
return fmt.Errorf("can't extract zip file: %w", err)
138+
return fmt.Errorf("can't extract compressed file: %w", err)
100139
}
101140

102-
err = os.Rename(unzipFolder, u.externalUIPath)
141+
err = os.Rename(extractedFolder, u.externalUIPath)
103142
if err != nil {
104143
return fmt.Errorf("rename UI folder failed: %w", err)
105144
}
@@ -122,9 +161,56 @@ func unzip(src, dest string) (string, error) {
122161
return "", err
123162
}
124163
defer r.Close()
164+
165+
// check whether or not only exists singleRoot dir
166+
rootDir := ""
167+
isSingleRoot := true
168+
for _, f := range r.File {
169+
parts := strings.Split(strings.Trim(f.Name, "/"), "/")
170+
if len(parts) == 0 {
171+
continue
172+
}
173+
174+
if len(parts) == 1 {
175+
isSingleRoot = false
176+
break
177+
}
178+
179+
if rootDir == "" {
180+
rootDir = parts[0]
181+
} else if parts[0] != rootDir {
182+
isSingleRoot = false
183+
break
184+
}
185+
}
186+
187+
// build the dir of extraction
125188
var extractedFolder string
189+
if isSingleRoot && rootDir != "" {
190+
// if the singleRoot, use it directly
191+
extractedFolder = filepath.Join(dest, rootDir)
192+
} else {
193+
// or put the files/dirs into new dir
194+
baseName := filepath.Base(src)
195+
baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
196+
extractedFolder = filepath.Join(dest, baseName)
197+
198+
for i := 1; ; i++ {
199+
if _, err := os.Stat(extractedFolder); os.IsNotExist(err) {
200+
break
201+
}
202+
extractedFolder = filepath.Join(dest, fmt.Sprintf("%s_%d", baseName, i))
203+
}
204+
}
205+
126206
for _, f := range r.File {
127-
fpath := filepath.Join(dest, f.Name)
207+
var fpath string
208+
if isSingleRoot && rootDir != "" {
209+
fpath = filepath.Join(dest, f.Name)
210+
} else {
211+
fpath = filepath.Join(extractedFolder, f.Name)
212+
}
213+
128214
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
129215
return "", fmt.Errorf("invalid file path: %s", fpath)
130216
}
@@ -149,13 +235,131 @@ func unzip(src, dest string) (string, error) {
149235
if err != nil {
150236
return "", err
151237
}
152-
if extractedFolder == "" {
153-
extractedFolder = filepath.Dir(fpath)
238+
}
239+
return extractedFolder, nil
240+
}
241+
242+
func untgz(src, dest string) (string, error) {
243+
file, err := os.Open(src)
244+
if err != nil {
245+
return "", err
246+
}
247+
defer file.Close()
248+
249+
gzr, err := gzip.NewReader(file)
250+
if err != nil {
251+
return "", err
252+
}
253+
defer gzr.Close()
254+
255+
tr := tar.NewReader(gzr)
256+
257+
rootDir := ""
258+
isSingleRoot := true
259+
for {
260+
header, err := tr.Next()
261+
if err == io.EOF {
262+
break
263+
}
264+
if err != nil {
265+
return "", err
266+
}
267+
268+
parts := strings.Split(strings.Trim(header.Name, "/"), "/")
269+
if len(parts) == 0 {
270+
continue
271+
}
272+
273+
if len(parts) == 1 {
274+
isSingleRoot = false
275+
break
276+
}
277+
278+
if rootDir == "" {
279+
rootDir = parts[0]
280+
} else if parts[0] != rootDir {
281+
isSingleRoot = false
282+
break
283+
}
284+
}
285+
286+
file.Seek(0, 0)
287+
gzr, _ = gzip.NewReader(file)
288+
tr = tar.NewReader(gzr)
289+
290+
var extractedFolder string
291+
if isSingleRoot && rootDir != "" {
292+
extractedFolder = filepath.Join(dest, rootDir)
293+
} else {
294+
baseName := filepath.Base(src)
295+
baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
296+
baseName = strings.TrimSuffix(baseName, ".tar")
297+
extractedFolder = filepath.Join(dest, baseName)
298+
299+
for i := 1; ; i++ {
300+
if _, err := os.Stat(extractedFolder); os.IsNotExist(err) {
301+
break
302+
}
303+
extractedFolder = filepath.Join(dest, fmt.Sprintf("%s_%d", baseName, i))
304+
}
305+
}
306+
307+
for {
308+
header, err := tr.Next()
309+
if err == io.EOF {
310+
break
311+
}
312+
if err != nil {
313+
return "", err
314+
}
315+
316+
var fpath string
317+
if isSingleRoot && rootDir != "" {
318+
fpath = filepath.Join(dest, header.Name)
319+
} else {
320+
fpath = filepath.Join(extractedFolder, header.Name)
321+
}
322+
323+
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
324+
return "", fmt.Errorf("invalid file path: %s", fpath)
325+
}
326+
327+
switch header.Typeflag {
328+
case tar.TypeDir:
329+
if err = os.MkdirAll(fpath, os.FileMode(header.Mode)); err != nil {
330+
return "", err
331+
}
332+
case tar.TypeReg:
333+
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
334+
return "", err
335+
}
336+
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode))
337+
if err != nil {
338+
return "", err
339+
}
340+
if _, err := io.Copy(outFile, tr); err != nil {
341+
outFile.Close()
342+
return "", err
343+
}
344+
outFile.Close()
154345
}
155346
}
156347
return extractedFolder, nil
157348
}
158349

350+
func extract(src, dest string) (string, error) {
351+
srcLower := strings.ToLower(src)
352+
switch {
353+
case strings.HasSuffix(srcLower, ".tar.gz") ||
354+
strings.HasSuffix(srcLower, ".tgz"):
355+
return untgz(src, dest)
356+
case strings.HasSuffix(srcLower, ".zip"):
357+
return unzip(src, dest)
358+
default:
359+
return "", fmt.Errorf("unsupported file format: %s", src)
360+
}
361+
}
362+
159363
func cleanup(root string) error {
160364
if _, err := os.Stat(root); os.IsNotExist(err) {
161365
return nil

0 commit comments

Comments
 (0)