From f2af587419fa034aeed1c2b0218cc9c2c04e222a Mon Sep 17 00:00:00 2001 From: Billy Owire Date: Mon, 2 Sep 2024 10:04:04 +0300 Subject: [PATCH] Fix WCOW COPY --from failure in multistage builds on Windows Fixes: #5193 Signed-off-by: Billy Owire --- cache/contenthash/checksum.go | 79 -------------------- cache/contenthash/checksum_unix.go | 94 ++++++++++++++++++++++++ cache/contenthash/checksum_windows.go | 100 ++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 79 deletions(-) create mode 100644 cache/contenthash/checksum_unix.go create mode 100644 cache/contenthash/checksum_windows.go diff --git a/cache/contenthash/checksum.go b/cache/contenthash/checksum.go index 7b592c794f8ed..968186036432e 100644 --- a/cache/contenthash/checksum.go +++ b/cache/contenthash/checksum.go @@ -1003,85 +1003,6 @@ var ( scanCounter atomic.Uint64 ) -func (cc *cacheContext) scanPath(ctx context.Context, m *mount, p string, followTrailing bool) (retErr error) { - p = path.Join("/", p) - - mp, err := m.mount(ctx) - if err != nil { - return err - } - - n := cc.tree.Root() - txn := cc.tree.Txn() - - resolvedPath, err := rootPath(mp, filepath.FromSlash(p), followTrailing, func(p, link string) error { - cr := &CacheRecord{ - Type: CacheRecordTypeSymlink, - Linkname: filepath.ToSlash(link), - } - p = path.Join("/", filepath.ToSlash(p)) - txn.Insert(convertPathToKey(p), cr) - return nil - }) - if err != nil { - return err - } - - // Scan the parent directory of the path we resolved, unless we're at the - // root (in which case we scan the root). - scanPath := filepath.Dir(resolvedPath) - if !strings.HasPrefix(filepath.ToSlash(scanPath)+"/", filepath.ToSlash(mp)+"/") { - scanPath = resolvedPath - } - - err = filepath.Walk(scanPath, func(itemPath string, fi os.FileInfo, err error) error { - if scanCounterEnable { - scanCounter.Add(1) - } - if err != nil { - // If the root doesn't exist, ignore the error. - if itemPath == scanPath && errors.Is(err, os.ErrNotExist) { - return nil - } - return errors.Wrapf(err, "failed to walk %s", itemPath) - } - rel, err := filepath.Rel(mp, itemPath) - if err != nil { - return err - } - k := convertPathToKey(keyPath(rel)) - if _, ok := n.Get(k); !ok { - cr := &CacheRecord{ - Type: CacheRecordTypeFile, - } - if fi.Mode()&os.ModeSymlink != 0 { - cr.Type = CacheRecordTypeSymlink - link, err := os.Readlink(itemPath) - if err != nil { - return err - } - cr.Linkname = filepath.ToSlash(link) - } - if fi.IsDir() { - cr.Type = CacheRecordTypeDirHeader - cr2 := &CacheRecord{ - Type: CacheRecordTypeDir, - } - txn.Insert(k, cr2) - k = append(k, 0) - } - txn.Insert(k, cr) - } - return nil - }) - if err != nil { - return err - } - - cc.tree = txn.Commit() - return nil -} - // followLinksCallback is called after we try to resolve each element. If the // path was not found, cr is nil. type followLinksCallback func(path string, cr *CacheRecord) error diff --git a/cache/contenthash/checksum_unix.go b/cache/contenthash/checksum_unix.go new file mode 100644 index 0000000000000..63e3200ee65e0 --- /dev/null +++ b/cache/contenthash/checksum_unix.go @@ -0,0 +1,94 @@ +//go:build !windows +// +build !windows + +package contenthash + +import ( + "context" + "os" + "path" + "path/filepath" + "strings" + + "github.com/pkg/errors" +) + +func (cc *cacheContext) scanPath(ctx context.Context, m *mount, p string, followTrailing bool) (retErr error) { + p = path.Join("/", p) + + mp, err := m.mount(ctx) + if err != nil { + return err + } + + n := cc.tree.Root() + txn := cc.tree.Txn() + + resolvedPath, err := rootPath(mp, filepath.FromSlash(p), followTrailing, func(p, link string) error { + cr := &CacheRecord{ + Type: CacheRecordTypeSymlink, + Linkname: filepath.ToSlash(link), + } + p = path.Join("/", filepath.ToSlash(p)) + txn.Insert(convertPathToKey(p), cr) + return nil + }) + if err != nil { + return err + } + + // Scan the parent directory of the path we resolved, unless we're at the + // root (in which case we scan the root). + scanPath := filepath.Dir(resolvedPath) + if !strings.HasPrefix(filepath.ToSlash(scanPath)+"/", filepath.ToSlash(mp)+"/") { + scanPath = resolvedPath + } + + err = filepath.Walk(scanPath, func(itemPath string, fi os.FileInfo, err error) error { + if scanCounterEnable { + scanCounter.Add(1) + } + if err != nil { + // If the root doesn't exist, ignore the error. + if itemPath == scanPath && errors.Is(err, os.ErrNotExist) { + return nil + } + return errors.Wrapf(err, "failed to walk %s", itemPath) + } + rel, err := filepath.Rel(mp, itemPath) + if err != nil { + return err + } + k := convertPathToKey(keyPath(rel)) + if _, ok := n.Get(k); !ok { + cr := &CacheRecord{ + Type: CacheRecordTypeFile, + } + if fi.Mode()&os.ModeSymlink != 0 { + cr.Type = CacheRecordTypeSymlink + link, err := os.Readlink(itemPath) + if err != nil { + return err + } + cr.Linkname = filepath.ToSlash(link) + } + if fi.IsDir() { + cr.Type = CacheRecordTypeDirHeader + cr2 := &CacheRecord{ + Type: CacheRecordTypeDir, + } + txn.Insert(k, cr2) + k = append(k, 0) + } + txn.Insert(k, cr) + } + return nil + }) + + if err != nil { + return err + } + + cc.tree = txn.Commit() + return nil +} diff --git a/cache/contenthash/checksum_windows.go b/cache/contenthash/checksum_windows.go new file mode 100644 index 0000000000000..3f1e04fa25c57 --- /dev/null +++ b/cache/contenthash/checksum_windows.go @@ -0,0 +1,100 @@ +//go:build windows +// +build windows + +package contenthash + +import ( + "context" + "os" + "path" + "path/filepath" + "strings" + + "github.com/Microsoft/go-winio" + "github.com/pkg/errors" +) + +func (cc *cacheContext) scanPath(ctx context.Context, m *mount, p string, followTrailing bool) (retErr error) { + p = path.Join("/", p) + + mp, err := m.mount(ctx) + if err != nil { + return err + } + + n := cc.tree.Root() + txn := cc.tree.Txn() + + resolvedPath, err := rootPath(mp, filepath.FromSlash(p), followTrailing, func(p, link string) error { + cr := &CacheRecord{ + Type: CacheRecordTypeSymlink, + Linkname: filepath.ToSlash(link), + } + p = path.Join("/", filepath.ToSlash(p)) + txn.Insert(convertPathToKey(p), cr) + return nil + }) + if err != nil { + return err + } + + // Scan the parent directory of the path we resolved, unless we're at the + // root (in which case we scan the root). + scanPath := filepath.Dir(resolvedPath) + if !strings.HasPrefix(filepath.ToSlash(scanPath)+"/", filepath.ToSlash(mp)+"/") { + scanPath = resolvedPath + } + + walkFn := func(itemPath string, fi os.FileInfo, err error) error { + if scanCounterEnable { + scanCounter.Add(1) + } + if err != nil { + // If the root doesn't exist, ignore the error. + if itemPath == scanPath && errors.Is(err, os.ErrNotExist) { + return nil + } + return errors.Wrapf(err, "failed to walk %s", itemPath) + } + rel, err := filepath.Rel(mp, itemPath) + if err != nil { + return err + } + k := convertPathToKey(keyPath(rel)) + if _, ok := n.Get(k); !ok { + cr := &CacheRecord{ + Type: CacheRecordTypeFile, + } + if fi.Mode()&os.ModeSymlink != 0 { + cr.Type = CacheRecordTypeSymlink + link, err := os.Readlink(itemPath) + if err != nil { + return err + } + cr.Linkname = filepath.ToSlash(link) + } + if fi.IsDir() { + cr.Type = CacheRecordTypeDirHeader + cr2 := &CacheRecord{ + Type: CacheRecordTypeDir, + } + txn.Insert(k, cr2) + k = append(k, 0) + } + txn.Insert(k, cr) + } + return nil + } + + privileges := []string{winio.SeBackupPrivilege} + err = winio.RunWithPrivileges(privileges, func() error { + return filepath.Walk(scanPath, walkFn) + }) + + if err != nil { + return err + } + + cc.tree = txn.Commit() + return nil +}