Skip to content
Merged
21 changes: 9 additions & 12 deletions client/llb/fileop.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ func Mkdir(p string, m os.FileMode, opt ...MkdirOption) *FileAction {
for _, o := range opt {
o.SetMkdirOption(&mi)
}

return &FileAction{
action: &fileActionMkdir{
file: p,
Expand Down Expand Up @@ -447,7 +448,6 @@ func Copy(input CopyInput, src, dest string, opts ...CopyOption) *FileAction {
for _, o := range opts {
o.SetCopyOption(&mi)
}

return &FileAction{
action: &fileActionCopy{
state: state,
Expand Down Expand Up @@ -523,22 +523,19 @@ func (a *fileActionCopy) toProtoAction(ctx context.Context, parent string, base

func (a *fileActionCopy) sourcePath(ctx context.Context) (string, error) {
p := path.Clean(a.src)
dir := "/"
var err error
if !path.IsAbs(p) {
if a.state != nil {
dir, err := a.state.GetDir(ctx)
if err != nil {
return "", err
}
p = path.Join("/", dir, p)
dir, err = a.state.GetDir(ctx)
} else if a.fas != nil {
dir, err := a.fas.state.GetDir(ctx)
if err != nil {
return "", err
}
p = path.Join("/", dir, p)
dir, err = a.fas.state.GetDir(ctx)
}
if err != nil {
return "", err
}
}
return p, nil
return path.Join(dir, p), nil
}

func (a *fileActionCopy) addCaps(f *FileOp) {
Expand Down
3 changes: 2 additions & 1 deletion client/llb/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/google/shlex"
"github.com/moby/buildkit/solver/pb"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)

type contextKeyT string
Expand Down Expand Up @@ -78,7 +79,7 @@ func dirf(value string, replace bool, v ...interface{}) StateOption {
if !path.IsAbs(value) {
prev, err := getDir(s)(ctx, c)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "getting dir from state")
}
if prev == "" {
prev = "/"
Expand Down
11 changes: 6 additions & 5 deletions client/llb/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestRelativeWd(t *testing.T) {
st := Scratch().Dir("foo")
require.Equal(t, getDirHelper(t, st), "/foo")
assert.Equal(t, getDirHelper(t, st), "/foo")

st = st.Dir("bar")
require.Equal(t, getDirHelper(t, st), "/foo/bar")
assert.Equal(t, getDirHelper(t, st), "/foo/bar")

st = st.Dir("..")
require.Equal(t, getDirHelper(t, st), "/foo")
assert.Equal(t, getDirHelper(t, st), "/foo")

st = st.Dir("/baz")
require.Equal(t, getDirHelper(t, st), "/baz")
assert.Equal(t, getDirHelper(t, st), "/baz")

st = st.Dir("../../..")
require.Equal(t, getDirHelper(t, st), "/")
assert.Equal(t, getDirHelper(t, st), "/")
}

func getDirHelper(t *testing.T, s State) string {
Expand Down
73 changes: 50 additions & 23 deletions frontend/dockerfile/dockerfile2llb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
if d.stage.BaseName == emptyImageName {
d.state = llb.Scratch()
d.image = emptyImage(platformOpt.targetPlatform)
d.platform = &platformOpt.targetPlatform
Copy link
Copy Markdown
Member

@tonistiigi tonistiigi Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this can be nil pointer dereference. In that nil case we should use DefaultSpec

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am reading the code correctly, the buildPlatforms field defaults to DefaultSpec if it's not set via ConvertOpt and targetPlatform is set to &buildPlatforms[0] if it's nil, before returning.

platformOpt seems to be set at the beginning of this function using buildPlatformOpt() which always seems to return a non-nil instance of the platformOpt type.

Would you prefer I add a nil pointer check here, in case buildPlatformOpt() changes at some point?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I read it wrong. This is not even a pointer, so it can't be nil.

continue
}
func(i int, d *dispatchState) {
Expand Down Expand Up @@ -478,11 +479,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS

// make sure that PATH is always set
if _, ok := shell.BuildEnvs(d.image.Config.Env)["PATH"]; !ok {
var pathOS string
if d.platform != nil {
pathOS = d.platform.OS
}
d.image.Config.Env = append(d.image.Config.Env, "PATH="+system.DefaultPathEnv(pathOS))
d.image.Config.Env = append(d.image.Config.Env, "PATH="+system.DefaultPathEnv(d.platform.OS))
}

// initialize base metadata from image conf
Expand Down Expand Up @@ -891,6 +888,7 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
st := llb.Scratch().Dir(sourcePath).File(
llb.Mkfile(f, 0755, []byte(data)),
dockerui.WithInternalName("preparing inline document"),
llb.Platform(*d.platform),
)

mount := llb.AddMount(destPath, st, llb.SourcePath(sourcePath), llb.Readonly)
Expand Down Expand Up @@ -994,12 +992,20 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
}

func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bool, opt *dispatchOpt) error {
d.state = d.state.Dir(c.Path)
wd := c.Path
if !path.IsAbs(c.Path) {
wd = path.Join("/", d.image.Config.WorkingDir, wd)
wd, err := system.NormalizeWorkdir(d.image.Config.WorkingDir, c.Path, d.platform.OS)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up: Naming is confusing here NormalizeWorkdir returns OS-specific paths and NormalizePath Unix-only paths. "Normalize" should mean the same thing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am terrible at naming things, and can't really think of a better name right now. I did make sure that the doc string for the function does state explicitly that for Windows, we convert path delimiter with \. If it's alright with you, I'd like to revisit the name at a later time. Or if you have any suggestions for a better name, I'll gladly change it.

if err != nil {
return errors.Wrap(err, "normalizing workdir")
}

// NormalizeWorkdir returns paths with platform specific separators. For Windows
// this will be of the form: \some\path, which is needed later when we pass it to
// HCS.
d.image.Config.WorkingDir = wd

// From this point forward, we can use UNIX style paths.
wd = system.ToSlash(wd, d.platform.OS)
d.state = d.state.Dir(wd)

if commit {
withLayer := false
if wd != "/" {
Expand All @@ -1018,6 +1024,7 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
d.state = d.state.File(llb.Mkdir(wd, 0755, mkdirOpt...),
llb.WithCustomName(prefixCommand(d, uppercaseCmd(processCmdEnv(opt.shlex, c.String(), env)), d.prefixPlatform, &platform, env)),
location(opt.sourceMap, c.Location()),
llb.Platform(*d.platform),
)
withLayer = true
}
Expand All @@ -1027,11 +1034,11 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
}

func dispatchCopy(d *dispatchState, cfg copyConfig) error {
pp, err := pathRelativeToWorkingDir(d.state, cfg.params.DestPath)
dest, err := pathRelativeToWorkingDir(d.state, cfg.params.DestPath, *d.platform)
if err != nil {
return err
}
dest := path.Join("/", pp)

if cfg.params.DestPath == "." || cfg.params.DestPath == "" || cfg.params.DestPath[len(cfg.params.DestPath)-1] == filepath.Separator {
dest += string(filepath.Separator)
}
Expand Down Expand Up @@ -1135,6 +1142,11 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
a = a.Copy(st, f, dest, opts...)
}
} else {
src, err = system.NormalizePath("/", src, d.platform.OS, false)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused why are doing these path transformations twice. If we already are always converting paths to UNIX in Dockerfile before passing to the LLB then why are we still carrying OS value in LLB and doing extra conversions there.

Copy link
Copy Markdown
Collaborator Author

@gabriel-samfira gabriel-samfira Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove the calls to NormalizePath() from client/llb and just use the paths as they come in, if you wish.

The current code in the master branch calls normalizePath(). This code is now incorporated into system.NormalizePath(), with additional logic to take into account the input platform.

Would you prefer we do away with this and just use path.Join() in toProtoAction() (example: https://github.com/moby/buildkit/blob/master/client/llb/fileop.go#L340) ?

It's fine with me, either way 😄

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, in the client I can just hardcode "linux" as the platform sent into NormalizePath() and it would behave just like the current normalizePath() in client/llb, but I am not sure ignoring the input platform (if set) is worth it. In any case, let me know what you prefer and I will make the change.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't need NormalizePath calls inside client/llb to make windows work then better to remove it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can revert the change to that file and bring back normalizePath() as it is in the master branch, because that is somewhat still needed. Although that code is now incorporated in system.NormalizePath().

Will change in the morning. I need to see if anything breaks (with a clear head).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reverted most of the changes to the client. Please take another look.

if err != nil {
return errors.Wrap(err, "removing drive letter")
}

opts := append([]llb.CopyOption{&llb.CopyInfo{
Mode: mode,
FollowSymlinks: true,
Expand All @@ -1146,9 +1158,9 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
}}, copyOpt...)

if a == nil {
a = llb.Copy(cfg.source, filepath.Join("/", src), dest, opts...)
a = llb.Copy(cfg.source, src, dest, opts...)
} else {
a = a.Copy(cfg.source, filepath.Join("/", src), dest, opts...)
a = a.Copy(cfg.source, src, dest, opts...)
}
}
}
Expand All @@ -1157,10 +1169,14 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
commitMessage.WriteString(" <<" + src.Path)

data := src.Data
f := src.Path
f, err := system.CheckSystemDriveAndRemoveDriveLetter(src.Path, d.platform.OS)
if err != nil {
return errors.Wrap(err, "removing drive letter")
}
st := llb.Scratch().File(
llb.Mkfile(f, 0664, []byte(data)),
dockerui.WithInternalName("preparing inline document"),
llb.Platform(*d.platform),
)

opts := append([]llb.CopyOption{&llb.CopyInfo{
Expand All @@ -1169,9 +1185,9 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
}}, copyOpt...)

if a == nil {
a = llb.Copy(st, f, dest, opts...)
a = llb.Copy(st, system.ToSlash(f, d.platform.OS), dest, opts...)
} else {
a = a.Copy(st, f, dest, opts...)
a = a.Copy(st, filepath.ToSlash(f), dest, opts...)
}
}

Expand Down Expand Up @@ -1202,7 +1218,9 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
d.cmdIndex-- // prefixCommand increases it
pgName := prefixCommand(d, name, d.prefixPlatform, &platform, env)

var copyOpts []llb.ConstraintsOpt
copyOpts := []llb.ConstraintsOpt{
llb.Platform(*d.platform),
}
copy(copyOpts, fileOpt)
copyOpts = append(copyOpts, llb.ProgressGroup(pgID, pgName, true))

Expand Down Expand Up @@ -1394,15 +1412,24 @@ func dispatchArg(d *dispatchState, c *instructions.ArgCommand, metaArgs []instru
return commitToHistory(&d.image, "ARG "+strings.Join(commitStrs, " "), false, nil, d.epoch)
}

func pathRelativeToWorkingDir(s llb.State, p string) (string, error) {
if path.IsAbs(p) {
return p, nil
}
dir, err := s.GetDir(context.TODO())
func pathRelativeToWorkingDir(s llb.State, p string, platform ocispecs.Platform) (string, error) {
dir, err := s.GetDir(context.TODO(), llb.Platform(platform))
if err != nil {
return "", err
}
return path.Join(dir, p), nil

if len(p) == 0 {
return dir, nil
}
p, err = system.CheckSystemDriveAndRemoveDriveLetter(p, platform.OS)
if err != nil {
return "", errors.Wrap(err, "removing drive letter")
}

if system.IsAbs(p, platform.OS) {
return system.NormalizePath("/", p, platform.OS, false)
}
return system.NormalizePath(dir, p, platform.OS, false)
}

func addEnv(env []string, k, v string) []string {
Expand Down
7 changes: 4 additions & 3 deletions frontend/gateway/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/moby/buildkit/session/secrets"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/system"

"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/executor"
Expand Down Expand Up @@ -92,7 +93,7 @@ func NewContainer(ctx context.Context, w worker.Worker, sm *session.Manager, g s
cm = refs[m.Input].Worker.CacheManager()
}
return cm.New(ctx, ref, g)
})
}, platform.OS)
if err != nil {
for i := len(p.Actives) - 1; i >= 0; i-- { // call in LIFO order
p.Actives[i].Ref.Release(context.TODO())
Expand Down Expand Up @@ -142,7 +143,7 @@ type MountMutableRef struct {

type MakeMutable func(m *opspb.Mount, ref cache.ImmutableRef) (cache.MutableRef, error)

func PrepareMounts(ctx context.Context, mm *mounts.MountManager, cm cache.Manager, g session.Group, cwd string, mnts []*opspb.Mount, refs []*worker.WorkerRef, makeMutable MakeMutable) (p PreparedMounts, err error) {
func PrepareMounts(ctx context.Context, mm *mounts.MountManager, cm cache.Manager, g session.Group, cwd string, mnts []*opspb.Mount, refs []*worker.WorkerRef, makeMutable MakeMutable, platform string) (p PreparedMounts, err error) {
// loop over all mounts, fill in mounts, root and outputs
for i, m := range mnts {
var (
Expand Down Expand Up @@ -265,7 +266,7 @@ func PrepareMounts(ctx context.Context, mm *mounts.MountManager, cm cache.Manage
} else {
mws := MountWithSession(mountable, g)
dest := m.Dest
if !filepath.IsAbs(filepath.Clean(dest)) {
if !system.IsAbs(filepath.Clean(dest), platform) {
dest = filepath.Join("/", cwd, dest)
}
mws.Dest = dest
Expand Down
51 changes: 33 additions & 18 deletions solver/llbsolver/file/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"os"
"path/filepath"
"runtime"
"strings"
"time"

Expand All @@ -13,6 +14,7 @@ import (
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/system"
"github.com/pkg/errors"
copy "github.com/tonistiigi/fsutil/copy"
)
Expand Down Expand Up @@ -66,7 +68,7 @@ func mapUserToChowner(user *copy.User, idmap *idtools.IdentityMapping) (copy.Cho
}

func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy.User, idmap *idtools.IdentityMapping) error {
p, err := fs.RootPath(d, filepath.Join("/", action.Path))
p, err := fs.RootPath(d, action.Path)
if err != nil {
return err
}
Expand Down Expand Up @@ -126,7 +128,10 @@ func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *cop

func rm(ctx context.Context, d string, action pb.FileActionRm) error {
if action.AllowWildcard {
src := cleanPath(action.Path)
src, err := cleanPath(action.Path)
if err != nil {
return errors.Wrap(err, "cleaning path")
}
m, err := copy.ResolveWildcards(d, src, false)
if err != nil {
return err
Expand Down Expand Up @@ -167,9 +172,14 @@ func rmPath(root, src string, allowNotFound bool) error {
}

func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *copy.User, idmap *idtools.IdentityMapping) error {
srcPath := cleanPath(action.Src)
destPath := cleanPath(action.Dest)

srcPath, err := cleanPath(action.Src)
if err != nil {
return errors.Wrap(err, "cleaning source path")
}
destPath, err := cleanPath(action.Dest)
if err != nil {
return errors.Wrap(err, "cleaning path")
}
if !action.CreateDestPath {
p, err := fs.RootPath(dest, filepath.Join("/", action.Dest))
if err != nil {
Expand Down Expand Up @@ -244,19 +254,6 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *
return nil
}

func cleanPath(s string) string {
s2 := filepath.Join("/", s)
if strings.HasSuffix(s, "/.") {
if s2 != "/" {
s2 += "/"
}
s2 += "."
} else if strings.HasSuffix(s, "/") && s2 != "/" {
s2 += "/"
}
return s2
}

type Backend struct {
}

Expand Down Expand Up @@ -349,3 +346,21 @@ func (fb *Backend) Copy(ctx context.Context, m1, m2, user, group fileoptypes.Mou

return docopy(ctx, src, dest, action, u, mnt2.m.IdentityMapping())
}

func cleanPath(s string) (string, error) {
s, err := system.CheckSystemDriveAndRemoveDriveLetter(s, runtime.GOOS)
if err != nil {
return "", errors.Wrap(err, "removing drive letter")
}
s = filepath.FromSlash(s)
s2 := filepath.Join("/", s)
if strings.HasSuffix(s, string(filepath.Separator)+".") {
if s2 != string(filepath.Separator) {
s2 += string(filepath.Separator)
}
s2 += "."
} else if strings.HasSuffix(s, string(filepath.Separator)) && s2 != string(filepath.Separator) {
s2 += string(filepath.Separator)
}
return s2, nil
}
Loading