diff --git a/pkg/util/fs_util.go b/pkg/util/fs_util.go index e968d62e8e..a27d531bb4 100644 --- a/pkg/util/fs_util.go +++ b/pkg/util/fs_util.go @@ -56,24 +56,30 @@ const ( type IgnoreListEntry struct { Path string PrefixMatchOnly bool + // AllowedPaths specifies **exact matches** to allow, even if they are under + // Path. + AllowedPaths []string } var defaultIgnoreList = []IgnoreListEntry{ { Path: filepath.Clean(config.KanikoDir), PrefixMatchOnly: false, + AllowedPaths: nil, }, { // similarly, we ignore /etc/mtab, since there is no way to know if the file was mounted or came // from the base image Path: "/etc/mtab", PrefixMatchOnly: false, + AllowedPaths: nil, }, { // we ingore /tmp/apt-key-gpghome, since the apt keys are added temporarily in this directory. // from the base image Path: "/tmp/apt-key-gpghome", PrefixMatchOnly: true, + AllowedPaths: nil, }, } @@ -111,6 +117,7 @@ func AddToIgnoreList(entry IgnoreListEntry) { ignorelist = append(ignorelist, IgnoreListEntry{ Path: filepath.Clean(entry.Path), PrefixMatchOnly: entry.PrefixMatchOnly, + AllowedPaths: nil, }) } @@ -118,9 +125,21 @@ func AddToDefaultIgnoreList(entry IgnoreListEntry) { defaultIgnoreList = append(defaultIgnoreList, IgnoreListEntry{ Path: filepath.Clean(entry.Path), PrefixMatchOnly: entry.PrefixMatchOnly, + AllowedPaths: nil, }) } +func AddAllowedPathToDefaultIgnoreList(allowPath string) error { + for _, ile := range defaultIgnoreList { + if !strings.HasPrefix(allowPath, ile.Path) { + continue + } + ile.AllowedPaths = append(ile.AllowedPaths, allowPath) + return nil + } + return fmt.Errorf("path %q is not covered by any current entry in ignore list", allowPath) +} + func IncludeWhiteout() FSOpt { return func(opts *FSConfig) { opts.includeWhiteout = true @@ -500,6 +519,11 @@ func IsInIgnoreList(path string) bool { func CheckCleanedPathAgainstProvidedIgnoreList(path string, wl []IgnoreListEntry) bool { for _, wl := range ignorelist { + for _, ap := range wl.AllowedPaths { + if ap == path { + return false + } + } if hasCleanedFilepathPrefix(path, wl.Path, wl.PrefixMatchOnly) { return true } @@ -556,6 +580,7 @@ func DetectFilesystemIgnoreList(path string) error { AddToIgnoreList(IgnoreListEntry{ Path: lineArr[4], PrefixMatchOnly: false, + AllowedPaths: nil, }) } if err == io.EOF { @@ -668,14 +693,30 @@ func CreateFile(path string, reader io.Reader, perm os.FileMode, uid uint32, gid } } + var renamed string dest, err := os.Create(path) if err != nil { - return errors.Wrap(err, "creating file") + if !errors.Is(err, syscall.ETXTBSY) { + return errors.Wrap(err, "creating file") + } + // If the file is busy, just write to a temp file and + // move to dest. + dest, err = os.CreateTemp(os.TempDir(), "") + if err != nil { + return errors.Wrap(err, "create temp file") + } + defer os.Remove(dest.Name()) + renamed = dest.Name() } defer dest.Close() if _, err := io.Copy(dest, reader); err != nil { return errors.Wrap(err, "copying file") } + if renamed != "" { + if err := os.Rename(renamed, path); err != nil { + return errors.Wrap(err, "rename temp file") + } + } return setFilePermissions(path, perm, int(uid), int(gid)) } @@ -685,6 +726,7 @@ func AddVolumePathToIgnoreList(path string) { AddToIgnoreList(IgnoreListEntry{ Path: path, PrefixMatchOnly: true, + AllowedPaths: nil, }) volumes = append(volumes, path) } diff --git a/pkg/util/fs_util_test.go b/pkg/util/fs_util_test.go index c21c9bfa0f..3e34cd49c9 100644 --- a/pkg/util/fs_util_test.go +++ b/pkg/util/fs_util_test.go @@ -58,13 +58,13 @@ func Test_DetectFilesystemSkiplist(t *testing.T) { err := DetectFilesystemIgnoreList(path) expectedSkiplist := []IgnoreListEntry{ - {"/kaniko", false}, - {"/proc", false}, - {"/dev", false}, - {"/dev/pts", false}, - {"/sys", false}, - {"/etc/mtab", false}, - {"/tmp/apt-key-gpghome", true}, + {"/kaniko", false, nil}, + {"/proc", false, nil}, + {"/dev", false, nil}, + {"/dev/pts", false, nil}, + {"/sys", false, nil}, + {"/etc/mtab", false, nil}, + {"/tmp/apt-key-gpghome", true, nil}, } actualSkiplist := ignorelist sort.Slice(actualSkiplist, func(i, j int) bool { @@ -274,7 +274,7 @@ func Test_CheckIgnoreList(t *testing.T) { name: "file ignored", args: args{ path: "/foo", - ignorelist: []IgnoreListEntry{{"/foo", false}}, + ignorelist: []IgnoreListEntry{{"/foo", false, nil}}, }, want: true, }, @@ -282,7 +282,7 @@ func Test_CheckIgnoreList(t *testing.T) { name: "directory ignored", args: args{ path: "/foo/bar", - ignorelist: []IgnoreListEntry{{"/foo", false}}, + ignorelist: []IgnoreListEntry{{"/foo", false, nil}}, }, want: true, }, @@ -290,7 +290,7 @@ func Test_CheckIgnoreList(t *testing.T) { name: "grandparent ignored", args: args{ path: "/foo/bar/baz", - ignorelist: []IgnoreListEntry{{"/foo", false}}, + ignorelist: []IgnoreListEntry{{"/foo", false, nil}}, }, want: true, }, @@ -298,7 +298,7 @@ func Test_CheckIgnoreList(t *testing.T) { name: "sibling ignored", args: args{ path: "/foo/bar/baz", - ignorelist: []IgnoreListEntry{{"/foo/bat", false}}, + ignorelist: []IgnoreListEntry{{"/foo/bat", false, nil}}, }, want: false, }, @@ -306,7 +306,7 @@ func Test_CheckIgnoreList(t *testing.T) { name: "prefix match only ", args: args{ path: "/tmp/apt-key-gpghome.xft/gpg.key", - ignorelist: []IgnoreListEntry{{"/tmp/apt-key-gpghome.*", true}}, + ignorelist: []IgnoreListEntry{{"/tmp/apt-key-gpghome.*", true, nil}}, }, want: true, }, @@ -1487,22 +1487,27 @@ func TestInitIgnoreList(t *testing.T) { { Path: "/kaniko", PrefixMatchOnly: false, + AllowedPaths: nil, }, { Path: "/test/kaniko", PrefixMatchOnly: false, + AllowedPaths: nil, }, { Path: "/test/proc", PrefixMatchOnly: false, + AllowedPaths: nil, }, { Path: "/etc/mtab", PrefixMatchOnly: false, + AllowedPaths: nil, }, { Path: "/tmp/apt-key-gpghome", PrefixMatchOnly: true, + AllowedPaths: nil, }, }