From 4489f39e5db6aa4ef78698af7a112d33b95a133c Mon Sep 17 00:00:00 2001 From: "guillaume.tardif" Date: Thu, 5 Nov 2020 16:08:39 +0100 Subject: [PATCH 1/4] duplicate windows os/exec.LookPath() and do not resolve files in current working dir if CWD is not explicitly in PATH. Signed-off-by: guillaume.tardif --- cli/mobycli/exec.go | 12 ++++- cli/mobycli/lp_unix.go | 28 ++++++++++ cli/mobycli/lp_windows.go | 111 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 cli/mobycli/lp_unix.go create mode 100644 cli/mobycli/lp_windows.go diff --git a/cli/mobycli/exec.go b/cli/mobycli/exec.go index 8c4500050..5d1cbc2a4 100644 --- a/cli/mobycli/exec.go +++ b/cli/mobycli/exec.go @@ -22,6 +22,7 @@ import ( "os" "os/exec" "os/signal" + "runtime" "strings" "github.com/spf13/cobra" @@ -60,7 +61,16 @@ func mustDelegateToMoby(ctxType string) bool { // Exec delegates to com.docker.cli if on moby context func Exec(root *cobra.Command) { - cmd := exec.Command(ComDockerCli, os.Args[1:]...) + execBinary := ComDockerCli + if runtime.GOOS == "windows" { // workaround for windows issue https://github.com/golang/go/issues/38736 + var err error + execBinary, err = LookPath(ComDockerCli) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + } + cmd := exec.Command(execBinary, os.Args[1:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/cli/mobycli/lp_unix.go b/cli/mobycli/lp_unix.go new file mode 100644 index 000000000..56b1eef65 --- /dev/null +++ b/cli/mobycli/lp_unix.go @@ -0,0 +1,28 @@ +// +build !windows + +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package mobycli + +import ( + "os/exec" +) + +// LookPath simply delegate to os/exec.LookPath if not on windows +func LookPath(file string) (string, error) { + return exec.LookPath(file) +} diff --git a/cli/mobycli/lp_windows.go b/cli/mobycli/lp_windows.go new file mode 100644 index 000000000..3fd1255fb --- /dev/null +++ b/cli/mobycli/lp_windows.go @@ -0,0 +1,111 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package mobycli + +import ( + "errors" + "os" + "os/exec" + "path/filepath" + "strings" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +var ErrNotFound = errors.New("executable file not found in %PATH%") + +func chkStat(file string) error { + d, err := os.Stat(file) + if err != nil { + return err + } + if d.IsDir() { + return os.ErrPermission + } + return nil +} + +func hasExt(file string) bool { + i := strings.LastIndex(file, ".") + if i < 0 { + return false + } + return strings.LastIndexAny(file, `:\/`) < i +} + +func findExecutable(file string, exts []string) (string, error) { + if len(exts) == 0 { + return file, chkStat(file) + } + if hasExt(file) { + if chkStat(file) == nil { + return file, nil + } + } + for _, e := range exts { + if f := file + e; chkStat(f) == nil { + return f, nil + } + } + return "", os.ErrNotExist +} + +// LookPath searches for an executable named file in the +// directories named by the PATH environment variable. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// LookPath also uses PATHEXT environment variable to match +// a suitable candidate. +// The result may be an absolute path or a path relative to the current directory. +func LookPath(file string) (string, error) { + var exts []string + x := os.Getenv(`PATHEXT`) + if x != "" { + for _, e := range strings.Split(strings.ToLower(x), `;`) { + if e == "" { + continue + } + if e[0] != '.' { + e = "." + e + } + exts = append(exts, e) + } + } else { + exts = []string{".com", ".exe", ".bat", ".cmd"} + } + + if strings.ContainsAny(file, `:\/`) { + if f, err := findExecutable(file, exts); err == nil { + return f, nil + } else { + return "", &exec.Error{file, err} + } + } + // DO NOT lookup current folder + //if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { + // return f, nil + //} + path := os.Getenv("path") + for _, dir := range filepath.SplitList(path) { + // empty dir means dupicate semicolon in PATH, should not resolve files in current working dir... + if strings.TrimSpace(dir) == "" { + continue + } + if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { + return f, nil + } + } + return "", &exec.Error{file, ErrNotFound} +} From 393682667339c7635c9d0dd8bb7f709484c624ab Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Thu, 5 Nov 2020 17:05:09 +0100 Subject: [PATCH 2/4] Keep original license in windows lp_windows.go Signed-off-by: Guillaume Tardif --- cli/mobycli/lp_windows.go | 35 +++++++++++++++++++++++++---------- scripts/validate/fileheader | 2 +- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/cli/mobycli/lp_windows.go b/cli/mobycli/lp_windows.go index 3fd1255fb..9fcafaa60 100644 --- a/cli/mobycli/lp_windows.go +++ b/cli/mobycli/lp_windows.go @@ -1,17 +1,31 @@ /* - Copyright 2020 Docker Compose CLI authors +Copyright (c) 2009 The Go Authors. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - http://www.apache.org/licenses/LICENSE-2.0 +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +* Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package mobycli @@ -93,6 +107,7 @@ func LookPath(file string) (string, error) { return "", &exec.Error{file, err} } } + // See https://github.com/golang/go/issues/38736 // DO NOT lookup current folder //if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { // return f, nil diff --git a/scripts/validate/fileheader b/scripts/validate/fileheader index b77b5a69f..d8a5a762a 100755 --- a/scripts/validate/fileheader +++ b/scripts/validate/fileheader @@ -24,4 +24,4 @@ fi BASEPATH="${1-}" -ltag -t "${BASEPATH}scripts/validate/template" -excludes "validate testdata" --check -v \ No newline at end of file +ltag -t "${BASEPATH}scripts/validate/template" -excludes "validate testdata cli/mobycli/lp_windows.go" --check -v \ No newline at end of file From 41a1553dabf3b1b158b2f180c5f495f8ecd3536e Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Thu, 5 Nov 2020 17:05:39 +0100 Subject: [PATCH 3/4] No specific case for windows, always resolve binary name before executing command Signed-off-by: Guillaume Tardif --- cli/mobycli/exec.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/cli/mobycli/exec.go b/cli/mobycli/exec.go index 5d1cbc2a4..de398785d 100644 --- a/cli/mobycli/exec.go +++ b/cli/mobycli/exec.go @@ -22,7 +22,6 @@ import ( "os" "os/exec" "os/signal" - "runtime" "strings" "github.com/spf13/cobra" @@ -61,14 +60,10 @@ func mustDelegateToMoby(ctxType string) bool { // Exec delegates to com.docker.cli if on moby context func Exec(root *cobra.Command) { - execBinary := ComDockerCli - if runtime.GOOS == "windows" { // workaround for windows issue https://github.com/golang/go/issues/38736 - var err error - execBinary, err = LookPath(ComDockerCli) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } + execBinary, err := LookPath(ComDockerCli) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } cmd := exec.Command(execBinary, os.Args[1:]...) cmd.Stdin = os.Stdin @@ -93,7 +88,7 @@ func Exec(root *cobra.Command) { } }() - err := cmd.Run() + err = cmd.Run() childExit <- true if err != nil { metrics.Track(store.DefaultContextType, os.Args[1:], metrics.FailureStatus) From 66a126334247499c57c94bb20369fa66e371b846 Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Thu, 5 Nov 2020 17:05:39 +0100 Subject: [PATCH 4/4] No specific case for windows, always resolve binary name before executing command Signed-off-by: Guillaume Tardif --- cli/mobycli/exec.go | 3 ++- cli/mobycli/{ => resolvepath}/lp_unix.go | 2 +- cli/mobycli/{ => resolvepath}/lp_windows.go | 2 +- scripts/validate/fileheader | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) rename cli/mobycli/{ => resolvepath}/lp_unix.go (97%) rename cli/mobycli/{ => resolvepath}/lp_windows.go (99%) diff --git a/cli/mobycli/exec.go b/cli/mobycli/exec.go index de398785d..15f875091 100644 --- a/cli/mobycli/exec.go +++ b/cli/mobycli/exec.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" + "github.com/docker/compose-cli/cli/mobycli/resolvepath" apicontext "github.com/docker/compose-cli/context" "github.com/docker/compose-cli/context/store" "github.com/docker/compose-cli/metrics" @@ -60,7 +61,7 @@ func mustDelegateToMoby(ctxType string) bool { // Exec delegates to com.docker.cli if on moby context func Exec(root *cobra.Command) { - execBinary, err := LookPath(ComDockerCli) + execBinary, err := resolvepath.LookPath(ComDockerCli) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/cli/mobycli/lp_unix.go b/cli/mobycli/resolvepath/lp_unix.go similarity index 97% rename from cli/mobycli/lp_unix.go rename to cli/mobycli/resolvepath/lp_unix.go index 56b1eef65..44caa3648 100644 --- a/cli/mobycli/lp_unix.go +++ b/cli/mobycli/resolvepath/lp_unix.go @@ -16,7 +16,7 @@ limitations under the License. */ -package mobycli +package resolvepath import ( "os/exec" diff --git a/cli/mobycli/lp_windows.go b/cli/mobycli/resolvepath/lp_windows.go similarity index 99% rename from cli/mobycli/lp_windows.go rename to cli/mobycli/resolvepath/lp_windows.go index 9fcafaa60..65d9b5b89 100644 --- a/cli/mobycli/lp_windows.go +++ b/cli/mobycli/resolvepath/lp_windows.go @@ -28,7 +28,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package mobycli +package resolvepath import ( "errors" diff --git a/scripts/validate/fileheader b/scripts/validate/fileheader index d8a5a762a..8b503df36 100755 --- a/scripts/validate/fileheader +++ b/scripts/validate/fileheader @@ -24,4 +24,4 @@ fi BASEPATH="${1-}" -ltag -t "${BASEPATH}scripts/validate/template" -excludes "validate testdata cli/mobycli/lp_windows.go" --check -v \ No newline at end of file +ltag -t "${BASEPATH}scripts/validate/template" -excludes "validate testdata resolvepath" --check -v \ No newline at end of file