mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-30 01:15:45 +08:00

Removes gogo/protobuf from buildx and updates to a version of moby/buildkit where gogo is removed. This also changes how the proto files are generated. This is because newer versions of protobuf are more strict about name conflicts. If two files have the same name (even if they are relative paths) and are used in different protoc commands, they'll conflict in the registry. Since protobuf file generation doesn't work very well with `paths=source_relative`, this removes the `go:generate` expression and just relies on the dockerfile to perform the generation. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
255 lines
5.3 KiB
Go
255 lines
5.3 KiB
Go
package fsutil
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/tonistiigi/fsutil/types"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
// Everything below is copied from containerd/fs. TODO: remove duplication @dmcgowan
|
|
|
|
// Const redefined because containerd/fs doesn't build on !linux
|
|
|
|
// ChangeKind is the type of modification that
|
|
// a change is making.
|
|
type ChangeKind int
|
|
|
|
const (
|
|
// ChangeKindAdd represents an addition of
|
|
// a file
|
|
ChangeKindAdd ChangeKind = iota
|
|
|
|
// ChangeKindModify represents a change to
|
|
// an existing file
|
|
ChangeKindModify
|
|
|
|
// ChangeKindDelete represents a delete of
|
|
// a file
|
|
ChangeKindDelete
|
|
)
|
|
|
|
func (k ChangeKind) String() string {
|
|
switch k {
|
|
case ChangeKindAdd:
|
|
return "add"
|
|
case ChangeKindModify:
|
|
return "modify"
|
|
case ChangeKindDelete:
|
|
return "delete"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// ChangeFunc is the type of function called for each change
|
|
// computed during a directory changes calculation.
|
|
type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
|
|
|
|
const compareChunkSize = 32 * 1024
|
|
|
|
type currentPath struct {
|
|
path string
|
|
stat *types.Stat
|
|
// fullPath string
|
|
}
|
|
|
|
// doubleWalkDiff walks both directories to create a diff
|
|
func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, filter FilterFunc, differ DiffType) (err error) {
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
var (
|
|
c1 = make(chan *currentPath, 128)
|
|
c2 = make(chan *currentPath, 128)
|
|
|
|
f1, f2 *currentPath
|
|
rmdir string
|
|
)
|
|
g.Go(func() error {
|
|
defer close(c1)
|
|
return a(ctx, c1)
|
|
})
|
|
g.Go(func() error {
|
|
defer close(c2)
|
|
return b(ctx, c2)
|
|
})
|
|
g.Go(func() error {
|
|
loop0:
|
|
for c1 != nil || c2 != nil {
|
|
if f1 == nil && c1 != nil {
|
|
f1, err = nextPath(ctx, c1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if f1 == nil {
|
|
c1 = nil
|
|
}
|
|
}
|
|
|
|
if f2 == nil && c2 != nil {
|
|
f2, err = nextPath(ctx, c2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if f2 == nil {
|
|
c2 = nil
|
|
}
|
|
}
|
|
if f1 == nil && f2 == nil {
|
|
continue
|
|
}
|
|
|
|
var f *types.Stat
|
|
var f2copy *currentPath
|
|
if f2 != nil {
|
|
statCopy := f2.stat.Clone()
|
|
if filter != nil {
|
|
filter(f2.path, statCopy)
|
|
}
|
|
f2copy = ¤tPath{path: f2.path, stat: statCopy}
|
|
}
|
|
k, p := pathChange(f1, f2copy)
|
|
switch k {
|
|
case ChangeKindAdd:
|
|
if rmdir != "" {
|
|
rmdir = ""
|
|
}
|
|
f = f2.stat
|
|
f2 = nil
|
|
case ChangeKindDelete:
|
|
// Check if this file is already removed by being
|
|
// under of a removed directory
|
|
if rmdir != "" && strings.HasPrefix(f1.path, rmdir) {
|
|
f1 = nil
|
|
continue
|
|
} else if rmdir == "" && f1.stat.IsDir() {
|
|
rmdir = f1.path + string(filepath.Separator)
|
|
} else if rmdir != "" {
|
|
rmdir = ""
|
|
}
|
|
f1 = nil
|
|
case ChangeKindModify:
|
|
same, err := sameFile(f1, f2copy, differ)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if f1.stat.IsDir() && !f2copy.stat.IsDir() {
|
|
rmdir = f1.path + string(filepath.Separator)
|
|
} else if rmdir != "" {
|
|
rmdir = ""
|
|
}
|
|
f = f2.stat
|
|
f1 = nil
|
|
f2 = nil
|
|
if same {
|
|
continue loop0
|
|
}
|
|
}
|
|
if err := changeFn(k, p, &StatInfo{f}, nil); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return g.Wait()
|
|
}
|
|
|
|
func pathChange(lower, upper *currentPath) (ChangeKind, string) {
|
|
if lower == nil {
|
|
if upper == nil {
|
|
panic("cannot compare nil paths")
|
|
}
|
|
return ChangeKindAdd, upper.path
|
|
}
|
|
if upper == nil {
|
|
return ChangeKindDelete, lower.path
|
|
}
|
|
|
|
switch i := ComparePath(lower.path, upper.path); {
|
|
case i < 0:
|
|
// File in lower that is not in upper
|
|
return ChangeKindDelete, lower.path
|
|
case i > 0:
|
|
// File in upper that is not in lower
|
|
return ChangeKindAdd, upper.path
|
|
default:
|
|
return ChangeKindModify, upper.path
|
|
}
|
|
}
|
|
|
|
func sameFile(f1, f2 *currentPath, differ DiffType) (same bool, retErr error) {
|
|
if differ == DiffNone {
|
|
return false, nil
|
|
}
|
|
// If not a directory also check size, modtime, and content
|
|
if !f1.stat.IsDir() {
|
|
if f1.stat.Size != f2.stat.Size {
|
|
return false, nil
|
|
}
|
|
|
|
if f1.stat.ModTime != f2.stat.ModTime {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
same, err := compareStat(f1.stat, f2.stat)
|
|
if err != nil || !same || differ == DiffMetadata {
|
|
return same, err
|
|
}
|
|
return compareFileContent(f1.path, f2.path)
|
|
}
|
|
|
|
func compareFileContent(p1, p2 string) (bool, error) {
|
|
f1, err := os.Open(p1)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer f1.Close()
|
|
f2, err := os.Open(p2)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer f2.Close()
|
|
|
|
b1 := make([]byte, compareChunkSize)
|
|
b2 := make([]byte, compareChunkSize)
|
|
for {
|
|
n1, err1 := f1.Read(b1)
|
|
if err1 != nil && err1 != io.EOF {
|
|
return false, err1
|
|
}
|
|
n2, err2 := f2.Read(b2)
|
|
if err2 != nil && err2 != io.EOF {
|
|
return false, err2
|
|
}
|
|
if n1 != n2 || !bytes.Equal(b1[:n1], b2[:n2]) {
|
|
return false, nil
|
|
}
|
|
if err1 == io.EOF && err2 == io.EOF {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// compareStat returns whether the stats are equivalent,
|
|
// whether the files are considered the same file, and
|
|
// an error
|
|
func compareStat(ls1, ls2 *types.Stat) (bool, error) {
|
|
return ls1.Mode == ls2.Mode && ls1.Uid == ls2.Uid && ls1.Gid == ls2.Gid && ls1.Devmajor == ls2.Devmajor && ls1.Devminor == ls2.Devminor && ls1.Linkname == ls2.Linkname, nil
|
|
}
|
|
|
|
func nextPath(ctx context.Context, pathC <-chan *currentPath) (*currentPath, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case p := <-pathC:
|
|
return p, nil
|
|
}
|
|
}
|