166 lines
4.7 KiB
Go
166 lines
4.7 KiB
Go
/*
|
|
Copyright 2020 Google LLC
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package filesystem
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/GoogleContainerTools/kaniko/pkg/config"
|
|
"github.com/GoogleContainerTools/kaniko/pkg/util"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ResolvePaths takes a slice of file paths and a list of skipped file paths. It resolve each
|
|
// file path according to a set of rules and then returns a slice of resolved paths or error.
|
|
// File paths are resolved according to the following rules:
|
|
// * If path is in ignorelist, skip it.
|
|
// * If path is a symlink, resolve it's ancestor link and add it to the output set.
|
|
// * If path is a symlink, resolve it's target. If the target is not ignored add it to the
|
|
// output set.
|
|
// * Add all ancestors of each path to the output set.
|
|
func ResolvePaths(paths []string, wl []util.IgnoreListEntry) (pathsToAdd []string, err error) {
|
|
logrus.Tracef("Resolving paths %s", paths)
|
|
|
|
fileSet := make(map[string]bool)
|
|
|
|
for _, f := range paths {
|
|
// If the given path is part of the ignorelist ignore it
|
|
if util.IsInProvidedIgnoreList(f, wl) {
|
|
logrus.Debugf("Path %s is in list to ignore, ignoring it", f)
|
|
continue
|
|
}
|
|
|
|
link, e := resolveSymlinkAncestor(f)
|
|
if e != nil {
|
|
continue
|
|
}
|
|
|
|
if f != link {
|
|
logrus.Tracef("Updated link %s to %s", f, link)
|
|
}
|
|
|
|
if !fileSet[link] {
|
|
pathsToAdd = append(pathsToAdd, link)
|
|
}
|
|
fileSet[link] = true
|
|
|
|
var evaled string
|
|
|
|
// If the path is a symlink we need to also consider the target of that
|
|
// link
|
|
evaled, e = filepath.EvalSymlinks(f)
|
|
if e != nil {
|
|
if !os.IsNotExist(e) {
|
|
logrus.Errorf("Couldn't eval %s with link %s", f, link)
|
|
return
|
|
}
|
|
|
|
logrus.Tracef("Symlink path %s, target does not exist", f)
|
|
continue
|
|
}
|
|
if f != evaled {
|
|
logrus.Tracef("Resolved symlink %s to %s", f, evaled)
|
|
}
|
|
|
|
// If the given path is a symlink and the target is part of the ignorelist
|
|
// ignore the target
|
|
if util.CheckProvidedIgnoreList(evaled, wl) {
|
|
logrus.Debugf("Path %s is ignored, ignoring it", evaled)
|
|
continue
|
|
}
|
|
|
|
if !fileSet[evaled] {
|
|
pathsToAdd = append(pathsToAdd, evaled)
|
|
}
|
|
fileSet[evaled] = true
|
|
}
|
|
|
|
// Also add parent directories to keep the permission of them correctly.
|
|
pathsToAdd = filesWithParentDirs(pathsToAdd)
|
|
return
|
|
}
|
|
|
|
// filesWithParentDirs returns every ancestor path for each provided file path.
|
|
// I.E. /foo/bar/baz/boom.txt => [/, /foo, /foo/bar, /foo/bar/baz, /foo/bar/baz/boom.txt]
|
|
func filesWithParentDirs(files []string) []string {
|
|
filesSet := map[string]bool{}
|
|
|
|
for _, file := range files {
|
|
file = filepath.Clean(file)
|
|
filesSet[file] = true
|
|
|
|
for _, dir := range util.ParentDirectories(file) {
|
|
dir = filepath.Clean(dir)
|
|
filesSet[dir] = true
|
|
}
|
|
}
|
|
|
|
newFiles := []string{}
|
|
for file := range filesSet {
|
|
newFiles = append(newFiles, file)
|
|
}
|
|
|
|
return newFiles
|
|
}
|
|
|
|
// resolveSymlinkAncestor returns the ancestor link of the provided symlink path or returns the
|
|
// the path if it is not a link. The ancestor link is the filenode whose type is a Symlink.
|
|
// E.G /baz/boom/bar.txt links to /usr/bin/bar.txt but /baz/boom/bar.txt itself is not a link.
|
|
// Instead /bar/boom is actually a link to /usr/bin. In this case resolveSymlinkAncestor would
|
|
// return /bar/boom.
|
|
func resolveSymlinkAncestor(path string) (string, error) {
|
|
if !filepath.IsAbs(path) {
|
|
return "", errors.New("dest path must be abs")
|
|
}
|
|
|
|
last := ""
|
|
newPath := filepath.Clean(path)
|
|
|
|
loop:
|
|
for newPath != config.RootDir {
|
|
fi, err := os.Lstat(newPath)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "resolvePaths: failed to lstat")
|
|
}
|
|
|
|
if util.IsSymlink(fi) {
|
|
last = filepath.Base(newPath)
|
|
newPath = filepath.Dir(newPath)
|
|
} else {
|
|
// Even if the filenode pointed to by newPath is a regular file,
|
|
// one of its ancestors could be a symlink. We call filepath.EvalSymlinks
|
|
// to test whether there are any links in the path. If the output of
|
|
// EvalSymlinks is different than the input we know one of the nodes in the
|
|
// the path is a link.
|
|
target, err := filepath.EvalSymlinks(newPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if target != newPath {
|
|
last = filepath.Base(newPath)
|
|
newPath = filepath.Dir(newPath)
|
|
} else {
|
|
break loop
|
|
}
|
|
}
|
|
}
|
|
newPath = filepath.Join(newPath, last)
|
|
return filepath.Clean(newPath), nil
|
|
}
|