Skip to content

Commit 92779e7

Browse files
committed
Handle case where symlink already exists in create-symlinks hook
Signed-off-by: Christopher Desiniotis <cdesiniotis@nvidia.com>
1 parent 23f1ba3 commit 92779e7

File tree

2 files changed

+75
-2
lines changed

2 files changed

+75
-2
lines changed

cmd/nvidia-cdi-hook/create-symlinks/create-symlinks.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package symlinks
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"os"
2223
"path/filepath"
@@ -25,6 +26,7 @@ import (
2526
"github.com/urfave/cli/v2"
2627

2728
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
29+
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
2830
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
2931
)
3032

@@ -121,8 +123,17 @@ func (m command) run(c *cli.Context, cfg *config) error {
121123
func (m command) createLink(containerRoot string, targetPath string, link string) error {
122124
linkPath := filepath.Join(containerRoot, link)
123125

126+
exists, err := doesLinkExist(targetPath, linkPath)
127+
if err != nil {
128+
return fmt.Errorf("failed to check if link exists: %w", err)
129+
}
130+
if exists {
131+
m.logger.Debugf("Link %s already exists", linkPath)
132+
return nil
133+
}
134+
124135
m.logger.Infof("Symlinking %v to %v", linkPath, targetPath)
125-
err := os.MkdirAll(filepath.Dir(linkPath), 0755)
136+
err = os.MkdirAll(filepath.Dir(linkPath), 0755)
126137
if err != nil {
127138
return fmt.Errorf("failed to create directory: %v", err)
128139
}
@@ -133,3 +144,19 @@ func (m command) createLink(containerRoot string, targetPath string, link string
133144

134145
return nil
135146
}
147+
148+
// doesLinkExist returns true if link exists and points to target.
149+
// An error is returned if link exists but points to a different target.
150+
func doesLinkExist(target string, link string) (bool, error) {
151+
currentTarget, err := symlinks.Resolve(link)
152+
if errors.Is(err, os.ErrNotExist) {
153+
return false, nil
154+
}
155+
if err != nil {
156+
return false, fmt.Errorf("failed to resolve existing symlink %s: %w", link, err)
157+
}
158+
if currentTarget == target {
159+
return true, nil
160+
}
161+
return true, fmt.Errorf("unexpected link target: %s", currentTarget)
162+
}

cmd/nvidia-cdi-hook/create-symlinks/create-symlinks_test.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,36 @@ import (
1212
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/symlinks"
1313
)
1414

15+
func TestDoesLinkExist(t *testing.T) {
16+
tmpDir := t.TempDir()
17+
18+
require.NoError(
19+
t,
20+
makeFs(tmpDir,
21+
dirOrLink{path: "/a/b/c", target: "d"},
22+
dirOrLink{path: "/a/b/e", target: "/a/b/f"},
23+
),
24+
)
25+
26+
exists, err := doesLinkExist("d", filepath.Join(tmpDir, "/a/b/c"))
27+
require.NoError(t, err)
28+
require.True(t, exists)
29+
30+
exists, err = doesLinkExist("/a/b/f", filepath.Join(tmpDir, "/a/b/e"))
31+
require.NoError(t, err)
32+
require.True(t, exists)
33+
34+
_, err = doesLinkExist("different-target", filepath.Join(tmpDir, "/a/b/c"))
35+
require.Error(t, err)
36+
37+
_, err = doesLinkExist("/a/b/d", filepath.Join(tmpDir, "/a/b/c"))
38+
require.Error(t, err)
39+
40+
exists, err = doesLinkExist("foo", filepath.Join(tmpDir, "/a/b/does-not-exist"))
41+
require.NoError(t, err)
42+
require.False(t, exists)
43+
}
44+
1545
func TestCreateLinkRelativePath(t *testing.T) {
1646
tmpDir := t.TempDir()
1747
hostRoot := filepath.Join(tmpDir, "/host-root/")
@@ -56,12 +86,28 @@ func TestCreateLinkAlreadyExists(t *testing.T) {
5686

5787
// nvidia-cdi-hook create-symlinks --link libfoo.so.1::/lib/libfoo.so
5888
err := getTestCommand().createLink(containerRoot, "libfoo.so.1", "/lib/libfoo.so")
59-
require.Error(t, err)
89+
require.NoError(t, err)
6090
target, err := symlinks.Resolve(filepath.Join(containerRoot, "lib/libfoo.so"))
6191
require.NoError(t, err)
6292
require.Equal(t, "libfoo.so.1", target)
6393
}
6494

95+
func TestCreateLinkAlreadyExistsDifferentTarget(t *testing.T) {
96+
tmpDir := t.TempDir()
97+
hostRoot := filepath.Join(tmpDir, "/host-root/")
98+
containerRoot := filepath.Join(tmpDir, "/container-root")
99+
100+
require.NoError(t, makeFs(hostRoot))
101+
require.NoError(t, makeFs(containerRoot, dirOrLink{path: "/lib/libfoo.so", target: "different-target"}))
102+
103+
// nvidia-cdi-hook create-symlinks --link libfoo.so.1::/lib/libfoo.so
104+
err := getTestCommand().createLink(containerRoot, "libfoo.so.1", "/lib/libfoo.so")
105+
require.Error(t, err)
106+
target, err := symlinks.Resolve(filepath.Join(containerRoot, "lib/libfoo.so"))
107+
require.NoError(t, err)
108+
require.Equal(t, "different-target", target)
109+
}
110+
65111
type dirOrLink struct {
66112
path string
67113
target string

0 commit comments

Comments
 (0)