Skip to content

Commit 98af90c

Browse files
authored
Add SubFS implementation for Melange (#1560)
Relates to: chainguard-dev/melange#1804 Relates to: chainguard-dev/melange#1731 This PR is part 1 of 2 (the second part is the Melange counterpart). We need a SubFS implementation in order to correctly identify packages that are built by Melange when using the memFS filesystem. Previously, we were doing `readlinkFS(pc.WorkspaceSubDir()` but now we need to maintain a FullFS type throughout the build. Using `fs.Sub` on the Melange side won't work because of this type assertion: https://github.com/chainguard-dev/apko/blob/3e63d01bb69480954b6040f5470197aead3f81da/pkg/apk/tarball/write.go#L250 If that assertion fails, then xattrs are not preserved. If there's an easier way to orchestrate this outside of adding an entire SubFS implementation please let myself or @stevebeattie know! Signed-off-by: egibs <[email protected]>
1 parent c524373 commit 98af90c

File tree

5 files changed

+203
-0
lines changed

5 files changed

+203
-0
lines changed

pkg/apk/fs/fs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type FullFS interface {
4545
GetXattr(path string, attr string) ([]byte, error)
4646
RemoveXattr(path string, attr string) error
4747
ListXattrs(path string) (map[string][]byte, error)
48+
Sub(path string) (FullFS, error)
4849
}
4950

5051
// File is an interface for a file. It includes Read, Write, Close.

pkg/apk/fs/memfs.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,36 @@ func (m *memFS) ListXattrs(path string) (map[string][]byte, error) {
524524
return ret, nil
525525
}
526526

527+
func Sub(fsys FullFS, dir string) (FullFS, error) {
528+
if !fs.ValidPath(dir) {
529+
return nil, &fs.PathError{Op: "sub", Path: dir, Err: fs.ErrInvalid}
530+
}
531+
if dir == "." {
532+
return fsys, nil
533+
}
534+
return &SubFS{fsys, dir}, nil
535+
}
536+
537+
func (m *memFS) Sub(path string) (FullFS, error) {
538+
cleanPath := filepath.Clean(path)
539+
if cleanPath == "." {
540+
return m, nil
541+
}
542+
543+
info, err := m.Stat(cleanPath)
544+
if err != nil {
545+
return nil, err
546+
}
547+
if !info.IsDir() {
548+
return nil, errors.New("not a directory")
549+
}
550+
551+
return &SubFS{
552+
FS: m,
553+
Root: cleanPath,
554+
}, nil
555+
}
556+
527557
type memFile struct {
528558
node *node
529559
fs *memFS

pkg/apk/fs/rwosfs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,9 @@ func (f *dirFS) RemoveXattr(path string, attr string) error {
547547
func (f *dirFS) ListXattrs(path string) (map[string][]byte, error) {
548548
return f.overrides.ListXattrs(path)
549549
}
550+
func (f *dirFS) Sub(path string) (FullFS, error) {
551+
return f.overrides.Sub(path)
552+
}
550553

551554
// sanitize ensures that we never go beyond the root of the filesystem
552555
func (f *dirFS) sanitizePath(p string) (v string, err error) {

pkg/apk/fs/sub.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package fs
2+
3+
import (
4+
"errors"
5+
"io/fs"
6+
"path/filepath"
7+
"time"
8+
)
9+
10+
type SubFS struct {
11+
FS FullFS
12+
Root string
13+
}
14+
15+
func (s *SubFS) Open(path string) (fs.File, error) {
16+
if !fs.ValidPath(path) {
17+
return nil, &fs.PathError{Op: "open", Path: path, Err: fs.ErrInvalid}
18+
}
19+
fullPath := filepath.Join(s.Root, path)
20+
return s.FS.Open(fullPath)
21+
}
22+
23+
func (s *SubFS) OpenReaderAt(path string) (File, error) {
24+
fullPath := filepath.Join(s.Root, path)
25+
return s.FS.OpenReaderAt(fullPath)
26+
}
27+
28+
func (s *SubFS) OpenFile(name string, flag int, perm fs.FileMode) (File, error) {
29+
fullPath := filepath.Join(s.Root, name)
30+
return s.FS.OpenFile(fullPath, flag, perm)
31+
}
32+
func (s *SubFS) Create(name string) (File, error) {
33+
fullPath := filepath.Join(s.Root, name)
34+
return s.FS.Create(fullPath)
35+
}
36+
37+
func (s *SubFS) ReadFile(name string) ([]byte, error) {
38+
fullPath := filepath.Join(s.Root, name)
39+
return s.FS.ReadFile(fullPath)
40+
}
41+
func (s *SubFS) WriteFile(name string, b []byte, mode fs.FileMode) error {
42+
fullPath := filepath.Join(s.Root, name)
43+
return s.FS.WriteFile(fullPath, b, mode)
44+
}
45+
46+
func (s *SubFS) Mkdir(path string, perm fs.FileMode) error {
47+
fullPath := filepath.Join(s.Root, path)
48+
return s.FS.Mkdir(fullPath, perm)
49+
}
50+
func (s *SubFS) MkdirAll(path string, perm fs.FileMode) error {
51+
fullPath := filepath.Join(s.Root, path)
52+
return s.FS.MkdirAll(fullPath, perm)
53+
}
54+
func (s *SubFS) ReadDir(name string) ([]fs.DirEntry, error) {
55+
fullPath := filepath.Join(s.Root, name)
56+
return s.FS.ReadDir(fullPath)
57+
}
58+
59+
func (s *SubFS) Stat(path string) (fs.FileInfo, error) {
60+
fullPath := filepath.Join(s.Root, path)
61+
return s.FS.Stat(fullPath)
62+
}
63+
func (s *SubFS) Lstat(path string) (fs.FileInfo, error) {
64+
fullPath := filepath.Join(s.Root, path)
65+
return s.FS.Lstat(fullPath)
66+
}
67+
68+
func (s *SubFS) Remove(name string) error {
69+
fullPath := filepath.Join(s.Root, name)
70+
return s.FS.Remove(fullPath)
71+
}
72+
func (s *SubFS) Chmod(path string, perm fs.FileMode) error {
73+
fullPath := filepath.Join(s.Root, path)
74+
return s.FS.Chmod(fullPath, perm)
75+
}
76+
func (s *SubFS) Chown(path string, uid int, gid int) error {
77+
fullPath := filepath.Join(s.Root, path)
78+
return s.FS.Chown(fullPath, uid, gid)
79+
}
80+
func (s *SubFS) Chtimes(path string, atime time.Time, mtime time.Time) error {
81+
fullPath := filepath.Join(s.Root, path)
82+
return s.FS.Chtimes(fullPath, atime, mtime)
83+
}
84+
85+
func (s *SubFS) Symlink(oldname, newname string) error {
86+
return s.FS.Symlink(oldname, newname)
87+
}
88+
func (s *SubFS) Link(oldname, newname string) error {
89+
return s.FS.Link(oldname, newname)
90+
}
91+
func (s *SubFS) Readlink(name string) (string, error) {
92+
fullPath := filepath.Join(s.Root, name)
93+
return s.FS.Readlink(fullPath)
94+
}
95+
96+
func (s *SubFS) Mknod(path string, mode uint32, dev int) error {
97+
fullPath := filepath.Join(s.Root, path)
98+
return s.FS.Mknod(fullPath, mode, dev)
99+
}
100+
func (s *SubFS) Readnod(path string) (int, error) {
101+
fullPath := filepath.Join(s.Root, path)
102+
return s.FS.Readnod(fullPath)
103+
}
104+
105+
func (s *SubFS) SetXattr(path string, attr string, data []byte) error {
106+
fullPath := filepath.Join(s.Root, path)
107+
return s.FS.SetXattr(fullPath, attr, data)
108+
}
109+
func (s *SubFS) GetXattr(path string, attr string) ([]byte, error) {
110+
fullPath := filepath.Join(s.Root, path)
111+
return s.FS.GetXattr(fullPath, attr)
112+
}
113+
114+
func (s *SubFS) RemoveXattr(path string, attr string) error {
115+
fullPath := filepath.Join(s.Root, path)
116+
return s.FS.RemoveXattr(fullPath, attr)
117+
}
118+
119+
func (s *SubFS) ListXattrs(path string) (map[string][]byte, error) {
120+
fullPath := filepath.Join(s.Root, path)
121+
return s.FS.ListXattrs(fullPath)
122+
}
123+
124+
func (s *SubFS) Sub(path string) (FullFS, error) {
125+
if !fs.ValidPath(path) {
126+
return nil, &fs.PathError{Op: "sub", Path: path, Err: fs.ErrInvalid}
127+
}
128+
129+
cleanPath := filepath.Clean(path)
130+
131+
if cleanPath == "." {
132+
return s, nil
133+
}
134+
135+
fullPath := filepath.Join(s.Root, cleanPath)
136+
info, err := s.FS.Stat(fullPath)
137+
if err != nil {
138+
return nil, err
139+
}
140+
if !info.IsDir() {
141+
return nil, errors.New("not a directory")
142+
}
143+
144+
return &SubFS{
145+
FS: s.FS,
146+
Root: fullPath,
147+
}, nil
148+
}

pkg/tarfs/fs.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,27 @@ func (m *memFS) ListXattrs(path string) (map[string][]byte, error) {
801801
return ret, nil
802802
}
803803

804+
func (m *memFS) Sub(path string) (apkfs.FullFS, error) {
805+
cleanPath := filepath.Clean(path)
806+
807+
if cleanPath == "." {
808+
return m, nil
809+
}
810+
811+
info, err := m.Stat(cleanPath)
812+
if err != nil {
813+
return nil, err
814+
}
815+
if !info.IsDir() {
816+
return nil, errors.New("not a directory")
817+
}
818+
819+
return &apkfs.SubFS{
820+
FS: m,
821+
Root: cleanPath,
822+
}, nil
823+
}
824+
804825
type memFile struct {
805826
node *node
806827
fs *memFS

0 commit comments

Comments
 (0)