Skip to content

Commit

Permalink
Fix WCOW COPY --from failure in multistage builds on Windows
Browse files Browse the repository at this point in the history
Fixes: #5193

Signed-off-by: Billy Owire <billyowire@microsoft.com>
  • Loading branch information
Billy Owire committed Sep 4, 2024
1 parent 94d3dae commit d60c5c4
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 79 deletions.
79 changes: 0 additions & 79 deletions cache/contenthash/checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
94 changes: 94 additions & 0 deletions cache/contenthash/checksum_unix.go
Original file line number Diff line number Diff line change
@@ -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
}
100 changes: 100 additions & 0 deletions cache/contenthash/checksum_windows.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit d60c5c4

Please sign in to comment.