diff --git a/pkg/commands/copy.go b/pkg/commands/copy.go index bb97bba32..6f1834def 100644 --- a/pkg/commands/copy.go +++ b/pkg/commands/copy.go @@ -65,12 +65,8 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu // For each source, iterate through and copy it over for _, src := range srcs { fullPath := filepath.Join(c.buildcontext, src) - srcPath, err := resolveIfSymlink(fullPath) - if err != nil { - return errors.Wrap(err, "resolving src symlink") - } - fi, err := os.Lstat(srcPath) + fi, err := os.Lstat(fullPath) if err != nil { return errors.Wrap(err, "could not copy source") } @@ -102,7 +98,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu c.snapshotFiles = append(c.snapshotFiles, copiedFiles...) } else if util.IsSymlink(fi) { // If file is a symlink, we want to copy the target file to destPath - exclude, err := util.CopySymlink(fullPath, destPath, c.buildcontext, uid, gid, false) + exclude, target, err := util.CopySymlink(fullPath, destPath, c.buildcontext, uid, gid) if err != nil { return errors.Wrap(err, "copying symlink") } @@ -110,6 +106,9 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu continue } c.snapshotFiles = append(c.snapshotFiles, destPath) + if target != "" { + c.snapshotFiles = append(c.snapshotFiles, target) + } } else { // ... Else, we want to copy over a file exclude, err := util.CopyFile(fullPath, destPath, c.buildcontext, uid, gid) diff --git a/pkg/commands/copy_test.go b/pkg/commands/copy_test.go index 5d665d60a..81fe58a60 100755 --- a/pkg/commands/copy_test.go +++ b/pkg/commands/copy_test.go @@ -465,8 +465,9 @@ func TestCopyCommand_ExecuteCommand_Extended(t *testing.T) { if err := ioutil.WriteFile(targetPath, []byte("woof"), 0777); err != nil { t.Fatal(err) } - os.Symlink(targetPath, filepath.Join(dir, "sym.link")) - + if err := os.Symlink("dam.txt", filepath.Join(dir, "sym.link")); err != nil { + t.Fatal(err) + } return testDir, filepath.Base(dir) } @@ -614,15 +615,59 @@ func TestCopyCommand_ExecuteCommand_Extended(t *testing.T) { if err != nil { t.Fatal(err) } - // sym.link should be present as a Regular file with contents of target "woof" - testutil.CheckDeepEqual(t, 1, len(files)) - testutil.CheckDeepEqual(t, files[0].Name(), "sym.link") - testutil.CheckDeepEqual(t, false, files[0].Mode()&os.ModeSymlink != 0) - c, err := ioutil.ReadFile(filepath.Join(testDir, "dest", "sym.link")) + // bam.txt and sym.link should be present + testutil.CheckDeepEqual(t, 2, len(files)) + testutil.CheckDeepEqual(t, files[0].Name(), "dam.txt") + testutil.CheckDeepEqual(t, files[1].Name(), "sym.link") + testutil.CheckDeepEqual(t, true, files[1].Mode()&os.ModeSymlink != 0) + linkName, err := os.Readlink(filepath.Join(testDir, "dest", "sym.link")) if err != nil { t.Fatal(err) } - testutil.CheckDeepEqual(t, "woof", string(c)) + testutil.CheckDeepEqual(t, linkName, "dam.txt") + }) + t.Run("copy deadlink symlink file to a dir", func(t *testing.T) { + testDir, srcDir := setupDirs(t) + defer os.RemoveAll(testDir) + doesNotExists := filepath.Join(testDir, "dead.txt") + if err := ioutil.WriteFile(doesNotExists, []byte("remove me"), 0777); err != nil { + t.Fatal(err) + } + if err := os.Symlink("../dead.txt", filepath.Join(testDir, srcDir, "dead.link")); err != nil { + t.Fatal(err) + } + if err := os.Remove(doesNotExists); err != nil { + t.Fatal(err) + } + + cmd := CopyCommand{ + cmd: &instructions.CopyCommand{ + SourcesAndDest: []string{filepath.Join(srcDir, "dead.link"), "dest/"}, + }, + buildcontext: testDir, + } + + cfg := &v1.Config{ + Cmd: nil, + Env: []string{}, + WorkingDir: testDir, + } + + err := cmd.ExecuteCommand(cfg, dockerfile.NewBuildArgs([]string{})) + testutil.CheckNoError(t, err) + // Check if "dest" dir exists with link dead.link + files, err := ioutil.ReadDir(filepath.Join(testDir, "dest")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, 1, len(files)) + testutil.CheckDeepEqual(t, files[0].Name(), "dead.link") + testutil.CheckDeepEqual(t, true, files[0].Mode()&os.ModeSymlink != 0) + linkName, err := os.Readlink(filepath.Join(testDir, "dest", "dead.link")) + if err != nil { + t.Fatal(err) + } + testutil.CheckDeepEqual(t, linkName, "../dead.txt") }) t.Run("copy src symlink dir to a dir", func(t *testing.T) { diff --git a/pkg/filesystem/resolve.go b/pkg/filesystem/resolve.go index a779e6e10..586e22095 100644 --- a/pkg/filesystem/resolve.go +++ b/pkg/filesystem/resolve.go @@ -47,8 +47,7 @@ func ResolvePaths(paths []string, wl []util.WhitelistEntry) (pathsToAdd []string link, e := resolveSymlinkAncestor(f) if e != nil { - err = errors.Wrap(e, "resolving symlinks") - return + continue } if f != link { diff --git a/pkg/util/.editorconfig b/pkg/util/.editorconfig new file mode 100644 index 000000000..c5319e7de --- /dev/null +++ b/pkg/util/.editorconfig @@ -0,0 +1,3 @@ +root = true + +[*] diff --git a/pkg/util/fs_util.go b/pkg/util/fs_util.go index b6ded4eba..fb275874b 100644 --- a/pkg/util/fs_util.go +++ b/pkg/util/fs_util.go @@ -595,7 +595,7 @@ func CopyDir(src, dest, buildcontext string, uid, gid int64) ([]string, error) { } } else if IsSymlink(fi) { // If file is a symlink, we want to create the same relative symlink - if _, err := CopySymlink(fullPath, destPath, buildcontext, uid, gid, true); err != nil { + if _, _, err := CopySymlink(fullPath, destPath, buildcontext, uid, gid); err != nil { return nil, err } } else { @@ -617,27 +617,31 @@ func CopyDir(src, dest, buildcontext string, uid, gid int64) ([]string, error) { // This done when copying a single file via // COPY sym.link dest // where sym.link is a link to target file. -func CopySymlink(src, dest, buildcontext string, uid int64, gid int64, asSymlink bool) (bool, error) { +func CopySymlink(src, dest, buildcontext string, uid int64, gid int64) (bool, string, error) { if ExcludeFile(src, buildcontext) { logrus.Debugf("%s found in .dockerignore, ignoring", src) - return true, nil - } - link, err := os.Readlink(src) - if err != nil { - logrus.Debugf("could not evaluate %s, probably a dead link", src) + return true, "", nil } if FilepathExists(dest) { if err := os.RemoveAll(dest); err != nil { - return false, err + return false, "", err } } if err := createParentDirectory(dest); err != nil { - return false, err + return false, "", err } - if asSymlink { - return true, os.Symlink(link, dest) + target := "" + link, err := os.Readlink(src) + if err != nil { + logrus.Debugf("could not read link for %s", src) + } else { + cpDest := filepath.Clean(filepath.Join(filepath.Dir(dest), link)) + srcLink := filepath.Clean(filepath.Join(filepath.Dir(src), link)) + if t, err := CopyFile(srcLink, cpDest, buildcontext, uid, gid); !t && err != nil { + target = link + } } - return CopyFile(src, dest, buildcontext, uid, gid) + return false, target, os.Symlink(link, dest) } // CopyFile copies the file at src to dest diff --git a/pkg/util/fs_util_test.go b/pkg/util/fs_util_test.go index fd401b770..fdda2018c 100644 --- a/pkg/util/fs_util_test.go +++ b/pkg/util/fs_util_test.go @@ -835,7 +835,7 @@ func TestCopySymlink(t *testing.T) { if err := os.Symlink(tc.linkTarget, link); err != nil { t.Fatal(err) } - if _, err := CopySymlink(link, dest, "", DoNotChangeUID, DoNotChangeGID, true); err != nil { + if _, _, err := CopySymlink(link, dest, "", DoNotChangeUID, DoNotChangeGID); err != nil { t.Fatal(err) } if _, err := os.Lstat(dest); err != nil {