Skip to content

Commit 773d748

Browse files
committed
fix(remount): relocate libraries along with their symlinks
1 parent ed52e3c commit 773d748

8 files changed

+655
-19
lines changed

envbuilder.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ ENTRYPOINT [%q]`, exePath, exePath, exePath)
438438
tempRemountDest := filepath.Join("/", MagicDir, "mnt")
439439
// ignorePrefixes is a superset of ignorePaths that we pass to kaniko's
440440
// IgnoreList.
441-
ignorePrefixes := append([]string{"/proc", "/sys"}, ignorePaths...)
441+
ignorePrefixes := append([]string{"/dev", "/proc", "/sys"}, ignorePaths...)
442442
restoreMounts, err := ebutil.TempRemount(options.Logger, tempRemountDest, ignorePrefixes...)
443443
defer func() { // restoreMounts should never be nil
444444
if err := restoreMounts(); err != nil {

internal/ebutil/libs.go

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package ebutil
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
)
9+
10+
// Based on https://github.com/NVIDIA/libnvidia-container/blob/v1.15.0/src/common.h#L29
11+
12+
const usrLibDir = "/usr/lib64"
13+
14+
const debianVersionFile = "/etc/debian_version"
15+
16+
// getLibDir returns the library directory. It returns a multiarch directory if
17+
// the distribution is Debian or a derivative.
18+
//
19+
// Based on https://github.com/NVIDIA/libnvidia-container/blob/v1.15.0/src/nvc_container.c#L152-L165
20+
func getLibDir(m mounter) (string, error) {
21+
// Debian and its derivatives use a multiarch directory scheme.
22+
if _, err := m.Stat(debianVersionFile); err != nil && !errors.Is(err, os.ErrNotExist) {
23+
return "", fmt.Errorf("check if debian: %w", err)
24+
} else if err == nil {
25+
return usrLibMultiarchDir, nil
26+
}
27+
28+
return usrLibDir, nil
29+
}
30+
31+
// getLibsSymlinks returns the stats for all library symlinks if the library
32+
// directory exists.
33+
func getLibsSymlinks(m mounter, libDir string) (map[string][]string, error) {
34+
des, err := m.ReadDir(libDir)
35+
if err != nil {
36+
return nil, fmt.Errorf("read directory %s: %w", libDir, err)
37+
}
38+
39+
libsSymlinks := make(map[string][]string)
40+
for _, de := range des {
41+
if de.IsDir() {
42+
continue
43+
}
44+
45+
if de.Type()&os.ModeSymlink != os.ModeSymlink {
46+
// Not a symlink. Skip.
47+
continue
48+
}
49+
50+
symlink := filepath.Join(libDir, de.Name())
51+
path, err := m.EvalSymlinks(symlink)
52+
if err != nil {
53+
return nil, fmt.Errorf("eval symlink %s: %w", symlink, err)
54+
}
55+
56+
path = filepath.Base(path)
57+
58+
if _, ok := libsSymlinks[path]; !ok {
59+
libsSymlinks[path] = make([]string, 0, 1)
60+
}
61+
62+
libsSymlinks[path] = append(libsSymlinks[path], de.Name())
63+
}
64+
65+
return libsSymlinks, nil
66+
}
67+
68+
// moveLibSymlinks moves a list of symlinks from source to destination directory.
69+
func moveLibSymlinks(m mounter, symlinks []string, srcDir, destDir string) error {
70+
for _, l := range symlinks {
71+
oldpath := filepath.Join(srcDir, l)
72+
newpath := filepath.Join(destDir, l)
73+
if err := m.Rename(oldpath, newpath); err != nil {
74+
return fmt.Errorf("move symlink %s => %s: %w", oldpath, newpath, err)
75+
}
76+
}
77+
return nil
78+
}

internal/ebutil/libs_amd64.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build amd64
2+
3+
package ebutil
4+
5+
// Based on https://github.com/NVIDIA/libnvidia-container/blob/v1.15.0/src/common.h#L36
6+
7+
const usrLibMultiarchDir = "/usr/lib/x86_64-linux-gnu"

internal/ebutil/libs_arm64.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build arm64
2+
3+
package ebutil
4+
5+
// Based on https://github.com/NVIDIA/libnvidia-container/blob/v1.15.0/src/common.h#L52
6+
7+
const usrLibMultiarchDir = "/usr/lib/aarch64-linux-gnu"

internal/ebutil/libs_ppc64le.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build ppc64le
2+
3+
package ebutil
4+
5+
// Based on https://github.com/NVIDIA/libnvidia-container/blob/v1.15.0/src/common.h#L44
6+
7+
const usrLibMultiarchDir = "/usr/lib/powerpc64le-linux-gnu"

internal/ebutil/mock_mounter_test.go

+44
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/ebutil/remount.go

+63-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ebutil
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67
"path/filepath"
@@ -44,15 +45,36 @@ func tempRemount(m mounter, logf func(notcodersdk.LogLevel, string, ...any), bas
4445
return func() error { return nil }, fmt.Errorf("get mounts: %w", err)
4546
}
4647

48+
libDir, err := getLibDir(m)
49+
if err != nil {
50+
return func() error { return nil }, fmt.Errorf("get lib directory: %w", err)
51+
}
52+
53+
libsSymlinks, err := getLibsSymlinks(m, libDir)
54+
if err != nil && !errors.Is(err, os.ErrNotExist) {
55+
return func() error { return nil }, fmt.Errorf("read lib symlinks: %w", err)
56+
}
57+
4758
// temp move of all ro mounts
4859
mounts := map[string]string{}
4960
var restoreOnce sync.Once
5061
var merr error
5162
// closer to attempt to restore original mount points
5263
restore = func() error {
5364
restoreOnce.Do(func() {
65+
if len(mounts) == 0 {
66+
return
67+
}
68+
69+
newLibDir, err := getLibDir(m)
70+
if err != nil {
71+
merr = multierror.Append(merr, fmt.Errorf("get new lib directory: %w", err))
72+
return
73+
}
74+
5475
for orig, moved := range mounts {
55-
if err := remount(m, moved, orig); err != nil {
76+
logf(notcodersdk.LogLevelTrace, "restore mount %s", orig)
77+
if err := remount(m, moved, orig, newLibDir, libsSymlinks); err != nil {
5678
merr = multierror.Append(merr, fmt.Errorf("restore mount: %w", err))
5779
}
5880
}
@@ -77,7 +99,8 @@ outer:
7799

78100
src := mountInfo.MountPoint
79101
dest := filepath.Join(base, src)
80-
if err := remount(m, src, dest); err != nil {
102+
logf(notcodersdk.LogLevelTrace, "temp remount %s", src)
103+
if err := remount(m, src, dest, libDir, libsSymlinks); err != nil {
81104
return restore, fmt.Errorf("temp remount: %w", err)
82105
}
83106

@@ -87,33 +110,51 @@ outer:
87110
return restore, nil
88111
}
89112

90-
func remount(m mounter, src, dest string) error {
113+
func remount(m mounter, src, dest, libDir string, libsSymlinks map[string][]string) error {
91114
stat, err := m.Stat(src)
92115
if err != nil {
93116
return fmt.Errorf("stat %s: %w", src, err)
94117
}
118+
95119
var destDir string
96120
if stat.IsDir() {
97121
destDir = dest
98122
} else {
99123
destDir = filepath.Dir(dest)
124+
if destDir == usrLibDir || destDir == usrLibMultiarchDir {
125+
// Restore mount to libDir
126+
destDir = libDir
127+
dest = filepath.Join(destDir, stat.Name())
128+
}
100129
}
130+
101131
if err := m.MkdirAll(destDir, 0o750); err != nil {
102132
return fmt.Errorf("ensure path: %w", err)
103133
}
134+
104135
if !stat.IsDir() {
105136
f, err := m.OpenFile(dest, os.O_CREATE, 0o640)
106137
if err != nil {
107138
return fmt.Errorf("ensure file path: %w", err)
108139
}
109-
defer f.Close()
140+
f.Close()
141+
142+
if symlinks, ok := libsSymlinks[stat.Name()]; ok {
143+
srcDir := filepath.Dir(src)
144+
if err := moveLibSymlinks(m, symlinks, srcDir, destDir); err != nil {
145+
return err
146+
}
147+
}
110148
}
149+
111150
if err := m.Mount(src, dest, "bind", syscall.MS_BIND, ""); err != nil {
112151
return fmt.Errorf("bind mount %s => %s: %w", src, dest, err)
113152
}
153+
114154
if err := m.Unmount(src, 0); err != nil {
115155
return fmt.Errorf("unmount orig src %s: %w", src, err)
116156
}
157+
117158
return nil
118159
}
119160

@@ -131,6 +172,12 @@ type mounter interface {
131172
Mount(string, string, string, uintptr, string) error
132173
// Unmount wraps syscall.Unmount
133174
Unmount(string, int) error
175+
// ReadDir wraps os.ReadDir
176+
ReadDir(string) ([]os.DirEntry, error)
177+
// EvalSymlinks wraps filepath.EvalSymlinks
178+
EvalSymlinks(string) (string, error)
179+
// Rename wraps os.Rename
180+
Rename(string, string) error
134181
}
135182

136183
// realMounter implements mounter and actually does the thing.
@@ -161,3 +208,15 @@ func (m *realMounter) OpenFile(name string, flag int, perm os.FileMode) (*os.Fil
161208
func (m *realMounter) Stat(path string) (os.FileInfo, error) {
162209
return os.Stat(path)
163210
}
211+
212+
func (m *realMounter) ReadDir(name string) ([]os.DirEntry, error) {
213+
return os.ReadDir(name)
214+
}
215+
216+
func (m *realMounter) EvalSymlinks(path string) (string, error) {
217+
return filepath.EvalSymlinks(path)
218+
}
219+
220+
func (m *realMounter) Rename(oldpath, newpath string) error {
221+
return os.Rename(oldpath, newpath)
222+
}

0 commit comments

Comments
 (0)