Skip to content

Commit eca23a9

Browse files
authored
Merge pull request #40 from kluctl/fix-modtimes
fix: Properly take file modification time into account when extracting
2 parents dedc148 + 5217d5c commit eca23a9

File tree

5 files changed

+37
-88
lines changed

5 files changed

+37
-88
lines changed

embed_util/embedded_files.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io/fs"
1010
"os"
1111
"path/filepath"
12+
"time"
1213
)
1314

1415
type EmbeddedFiles struct {
@@ -128,7 +129,7 @@ func (e *EmbeddedFiles) copyEmbeddedFilesToTmp(embedFs fs.FS, fl *fileList) erro
128129
if resolvedFle.Mode.Type() == existingSt.Mode().Type() {
129130
if resolvedFle.Mode.IsDir() {
130131
continue
131-
} else if existingSt.Size() == resolvedFle.Size {
132+
} else if existingSt.Size() == resolvedFle.Size && existingSt.ModTime().Unix() == resolvedFle.ModTime {
132133
// unchanged
133134
continue
134135
}
@@ -176,6 +177,12 @@ func (e *EmbeddedFiles) copyEmbeddedFilesToTmp(embedFs fs.FS, fl *fileList) erro
176177
if err != nil {
177178
return err
178179
}
180+
if !resolvedFle.Mode.IsDir() {
181+
err = os.Chtimes(path, time.Time{}, time.Unix(resolvedFle.ModTime, 0))
182+
if err != nil {
183+
return err
184+
}
185+
}
179186
}
180187

181188
return nil

embed_util/file_list.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type fileList struct {
2020
type fileListEntry struct {
2121
Name string `json:"name"`
2222
Size int64 `json:"size"`
23+
ModTime int64 `json:"modTime"`
2324
Mode fs.FileMode `json:"perm"`
2425
Symlink string `json:"symlink,omitempty"`
2526
Compressed bool `json:"compressed,omitempty"`
@@ -48,9 +49,10 @@ func buildFileListFromDir(dir string) (*fileList, error) {
4849
}
4950

5051
fle := fileListEntry{
51-
Name: relPath,
52-
Size: info.Size(),
53-
Mode: info.Mode(),
52+
Name: relPath,
53+
Size: info.Size(),
54+
ModTime: info.ModTime().Unix(),
55+
Mode: info.Mode(),
5456
}
5557

5658
if info.Mode().Type() == fs.ModeSymlink {
@@ -92,15 +94,17 @@ func buildFileListFromFs(embedFs fs.FS) (*fileList, error) {
9294
}
9395

9496
fle := fileListEntry{
95-
Name: path,
96-
Size: info.Size(),
97-
Mode: info.Mode() | 0o600,
97+
Name: path,
98+
Size: info.Size(),
99+
ModTime: info.ModTime().Unix(),
100+
Mode: info.Mode() | 0o600,
98101
}
99102

100103
if info.Mode().Type() == fs.ModeSymlink {
101104
return fmt.Errorf("symlink not supported in buildFileListFromFs")
102105
} else if info.Mode().IsDir() {
103106
fle.Size = 0
107+
fle.ModTime = 0
104108
}
105109

106110
fl.Files = append(fl.Files, fle)

go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ module github.com/kluctl/go-embed-python
33
go 1.19
44

55
require (
6-
github.com/cyphar/filepath-securejoin v0.2.4
76
github.com/gobwas/glob v0.2.3
8-
github.com/klauspost/compress v1.17.7
7+
github.com/klauspost/compress v1.17.8
98
github.com/rogpeppe/go-internal v1.12.0
109
github.com/sirupsen/logrus v1.9.3
1110
github.com/stretchr/testify v1.9.0
12-
golang.org/x/sync v0.6.0
11+
golang.org/x/sync v0.7.0
1312
)
1413

1514
require (

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
2-
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
31
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
42
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
53
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
64
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
75
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
86
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
97
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
8+
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
9+
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
1010
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1111
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1212
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
@@ -19,6 +19,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
1919
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
2020
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
2121
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
22+
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
23+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
2224
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2325
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
2426
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

internal/tar.go

Lines changed: 13 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ package internal
33
import (
44
"archive/tar"
55
"fmt"
6-
securejoin "github.com/cyphar/filepath-securejoin"
76
"io"
8-
"io/fs"
97
"os"
108
"path/filepath"
9+
"strings"
1110
)
1211

1312
func ExtractTarStream(r io.Reader, targetPath string) error {
@@ -22,12 +21,13 @@ func ExtractTarStream(r io.Reader, targetPath string) error {
2221
return fmt.Errorf("ExtractTarStream: Next() failed: %w", err)
2322
}
2423

25-
header.Name = filepath.FromSlash(header.Name)
26-
27-
p, err := securejoin.SecureJoin(targetPath, header.Name)
28-
if err != nil {
29-
return err
24+
if !validRelPath(header.Name) {
25+
return fmt.Errorf("tar contained invalid name error %q", header.Name)
3026
}
27+
28+
p := filepath.FromSlash(header.Name)
29+
p = filepath.Join(targetPath, p)
30+
3131
err = os.MkdirAll(filepath.Dir(p), 0755)
3232
if err != nil {
3333
return err
@@ -39,6 +39,7 @@ func ExtractTarStream(r io.Reader, targetPath string) error {
3939
return fmt.Errorf("ExtractTarStream: Mkdir() failed: %w", err)
4040
}
4141
case tar.TypeReg:
42+
_ = os.Remove(p) // we allow overwriting, which easily happens on case insensitive filesystems
4243
outFile, err := os.Create(p)
4344
if err != nil {
4445
return fmt.Errorf("ExtractTarStream: Create() failed: %w", err)
@@ -57,6 +58,7 @@ func ExtractTarStream(r io.Reader, targetPath string) error {
5758
return err
5859
}
5960
case tar.TypeSymlink:
61+
_ = os.Remove(p) // we allow overwriting, which easily happens on case insensitive filesystems
6062
if err := os.Symlink(header.Linkname, p); err != nil {
6163
return fmt.Errorf("ExtractTarStream: Symlink() failed: %w", err)
6264
}
@@ -67,74 +69,9 @@ func ExtractTarStream(r io.Reader, targetPath string) error {
6769
return nil
6870
}
6971

70-
func AddToTar(tw *tar.Writer, pth string, name string, filter func(h *tar.Header, size int64) (*tar.Header, error)) error {
71-
fi, err := os.Lstat(pth)
72-
if err != nil {
73-
return err
74-
}
75-
76-
var linkName string
77-
if fi.Mode().Type() == fs.ModeSymlink {
78-
x, err := os.Readlink(pth)
79-
if err != nil {
80-
return err
81-
}
82-
linkName = x
83-
}
84-
85-
h, err := tar.FileInfoHeader(fi, linkName)
86-
if err != nil {
87-
return err
88-
}
89-
h.Name = filepath.ToSlash(name)
90-
91-
if filter != nil {
92-
s := fi.Size()
93-
if fi.IsDir() {
94-
s = 0
95-
}
96-
h, err = filter(h, s)
97-
if err != nil {
98-
return err
99-
}
100-
if h == nil {
101-
return nil
102-
}
103-
}
104-
105-
err = tw.WriteHeader(h)
106-
if err != nil {
107-
return err
108-
}
109-
110-
if fi.Mode().Type() == fs.ModeSymlink {
111-
return nil
112-
}
113-
114-
if fi.Mode().IsDir() {
115-
des, err := os.ReadDir(pth)
116-
if err != nil {
117-
return err
118-
}
119-
for _, d := range des {
120-
err = AddToTar(tw, filepath.Join(pth, d.Name()), filepath.Join(name, d.Name()), filter)
121-
if err != nil {
122-
return err
123-
}
124-
}
125-
return nil
126-
} else if fi.Mode().IsRegular() {
127-
f, err := os.Open(pth)
128-
if err != nil {
129-
return err
130-
}
131-
defer f.Close()
132-
_, err = io.Copy(tw, f)
133-
if err != nil {
134-
return err
135-
}
136-
return nil
137-
} else {
138-
return fmt.Errorf("unsupported file type/mode %s", fi.Mode().String())
72+
func validRelPath(p string) bool {
73+
if p == "" || strings.Contains(p, `\`) || strings.HasPrefix(p, "/") || strings.Contains(p, "../") {
74+
return false
13975
}
76+
return true
14077
}

0 commit comments

Comments
 (0)