mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-18 17:27:49 +08:00
Merge pull request #1477 from crazy-max/git-wsl
build: lookup the right git binary on WSL
This commit is contained in:
commit
64e4c19971
@ -594,10 +594,6 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Op
|
|||||||
so.FrontendAttrs["attest:provenance"] = "mode=min,inline-only=true"
|
so.FrontendAttrs["attest:provenance"] = "mode=min,inline-only=true"
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range getGitAttributes(ctx, opt.Inputs.ContextPath, opt.Inputs.DockerfilePath) {
|
|
||||||
so.FrontendAttrs[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// set platforms
|
// set platforms
|
||||||
if len(opt.Platforms) != 0 {
|
if len(opt.Platforms) != 0 {
|
||||||
pp := make([]string, len(opt.Platforms))
|
pp := make([]string, len(opt.Platforms))
|
||||||
@ -852,6 +848,10 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
|
|||||||
for k, opt := range opt {
|
for k, opt := range opt {
|
||||||
multiDriver := len(m[k]) > 1
|
multiDriver := len(m[k]) > 1
|
||||||
hasMobyDriver := false
|
hasMobyDriver := false
|
||||||
|
gitattrs, err := getGitAttributes(ctx, opt.Inputs.ContextPath, opt.Inputs.DockerfilePath)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn(err)
|
||||||
|
}
|
||||||
for i, np := range m[k] {
|
for i, np := range m[k] {
|
||||||
node := nodes[np.driverIndex]
|
node := nodes[np.driverIndex]
|
||||||
if node.Driver.IsMobyDriver() {
|
if node.Driver.IsMobyDriver() {
|
||||||
@ -861,6 +861,9 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
|
|||||||
so, release, err := toSolveOpt(ctx, node, multiDriver, opt, np.bopts, configDir, w, func(name string) (io.WriteCloser, func(), error) {
|
so, release, err := toSolveOpt(ctx, node, multiDriver, opt, np.bopts, configDir, w, func(name string) (io.WriteCloser, func(), error) {
|
||||||
return docker.LoadImage(ctx, name, w)
|
return docker.LoadImage(ctx, name, w)
|
||||||
})
|
})
|
||||||
|
for k, v := range gitattrs {
|
||||||
|
so.FrontendAttrs[k] = v
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
72
build/git.go
72
build/git.go
@ -3,18 +3,19 @@ package build
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/buildx/util/gitutil"
|
"github.com/docker/buildx/util/gitutil"
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DockerfileLabel = "com.docker.image.source.entrypoint"
|
const DockerfileLabel = "com.docker.image.source.entrypoint"
|
||||||
|
|
||||||
func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (res map[string]string) {
|
func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (res map[string]string, _ error) {
|
||||||
res = make(map[string]string)
|
res = make(map[string]string)
|
||||||
if contextPath == "" {
|
if contextPath == "" {
|
||||||
return
|
return
|
||||||
@ -48,27 +49,50 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st
|
|||||||
wd, _ = filepath.Abs(filepath.Join(cwd, contextPath))
|
wd, _ = filepath.Abs(filepath.Join(cwd, contextPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
gitc := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd))
|
gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd))
|
||||||
if !gitc.IsInsideWorkTree() {
|
if err != nil {
|
||||||
logrus.Warnf("Unable to determine Git information")
|
if st, err := os.Stat(path.Join(wd, ".git")); err == nil && st.IsDir() {
|
||||||
|
return res, errors.New("git was not found in the system. Current commit information was not captured by the build")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var resRevision, resSource, resDockerfilePath string
|
if !gitc.IsInsideWorkTree() {
|
||||||
|
if st, err := os.Stat(path.Join(wd, ".git")); err == nil && st.IsDir() {
|
||||||
|
return res, errors.New("failed to read current commit information with git rev-parse --is-inside-work-tree")
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
if sha, err := gitc.FullCommit(); err == nil && sha != "" {
|
if sha, err := gitc.FullCommit(); err != nil {
|
||||||
resRevision = sha
|
return res, errors.Wrapf(err, "failed to get git commit")
|
||||||
|
} else if sha != "" {
|
||||||
if gitc.IsDirty() {
|
if gitc.IsDirty() {
|
||||||
resRevision += "-dirty"
|
sha += "-dirty"
|
||||||
|
}
|
||||||
|
if setGitLabels {
|
||||||
|
res["label:"+specs.AnnotationRevision] = sha
|
||||||
|
}
|
||||||
|
if setGitInfo {
|
||||||
|
res["vcs:revision"] = sha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rurl, err := gitc.RemoteURL(); err == nil && rurl != "" {
|
if rurl, err := gitc.RemoteURL(); err != nil {
|
||||||
resSource = rurl
|
return res, errors.Wrapf(err, "failed to get git remote url")
|
||||||
|
} else if rurl != "" {
|
||||||
|
if setGitLabels {
|
||||||
|
res["label:"+specs.AnnotationSource] = rurl
|
||||||
|
}
|
||||||
|
if setGitInfo {
|
||||||
|
res["vcs:source"] = rurl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if setGitLabels {
|
if setGitLabels {
|
||||||
if root, err := gitc.RootDir(); err == nil && root != "" {
|
if root, err := gitc.RootDir(); err != nil {
|
||||||
|
return res, errors.Wrapf(err, "failed to get git root dir")
|
||||||
|
} else if root != "" {
|
||||||
if dockerfilePath == "" {
|
if dockerfilePath == "" {
|
||||||
dockerfilePath = filepath.Join(wd, "Dockerfile")
|
dockerfilePath = filepath.Join(wd, "Dockerfile")
|
||||||
}
|
}
|
||||||
@ -78,32 +102,10 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st
|
|||||||
}
|
}
|
||||||
dockerfilePath, _ = filepath.Rel(root, dockerfilePath)
|
dockerfilePath, _ = filepath.Rel(root, dockerfilePath)
|
||||||
if !strings.HasPrefix(dockerfilePath, "..") {
|
if !strings.HasPrefix(dockerfilePath, "..") {
|
||||||
resDockerfilePath = dockerfilePath
|
res["label:"+DockerfileLabel] = dockerfilePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if resSource != "" {
|
|
||||||
if setGitLabels {
|
|
||||||
res["label:"+specs.AnnotationSource] = resSource
|
|
||||||
}
|
|
||||||
if setGitInfo {
|
|
||||||
res["vcs:source"] = resSource
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if resRevision != "" {
|
|
||||||
if setGitLabels {
|
|
||||||
res["label:"+specs.AnnotationRevision] = resRevision
|
|
||||||
}
|
|
||||||
if setGitInfo {
|
|
||||||
res["vcs:revision"] = resRevision
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if resDockerfilePath != "" {
|
|
||||||
if setGitLabels {
|
|
||||||
res["label:"+DockerfileLabel] = resDockerfilePath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package build
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -10,21 +11,42 @@ import (
|
|||||||
"github.com/docker/buildx/util/gitutil"
|
"github.com/docker/buildx/util/gitutil"
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupTest(tb testing.TB) {
|
func setupTest(tb testing.TB) {
|
||||||
gitutil.Mktmp(tb)
|
gitutil.Mktmp(tb)
|
||||||
gitutil.GitInit(tb)
|
|
||||||
|
c, err := gitutil.New()
|
||||||
|
require.NoError(tb, err)
|
||||||
|
gitutil.GitInit(c, tb)
|
||||||
|
|
||||||
df := []byte("FROM alpine:latest\n")
|
df := []byte("FROM alpine:latest\n")
|
||||||
assert.NoError(tb, os.WriteFile("Dockerfile", df, 0644))
|
assert.NoError(tb, os.WriteFile("Dockerfile", df, 0644))
|
||||||
gitutil.GitAdd(tb, "Dockerfile")
|
|
||||||
gitutil.GitCommit(tb, "initial commit")
|
gitutil.GitAdd(c, tb, "Dockerfile")
|
||||||
|
gitutil.GitCommit(c, tb, "initial commit")
|
||||||
|
gitutil.GitSetRemote(c, tb, "git@github.com:docker/buildx.git")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGitAttributesNotGitRepo(t *testing.T) {
|
||||||
|
_, err := getGitAttributes(context.Background(), t.TempDir(), "Dockerfile")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetGitAttributesBadGitRepo(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
require.NoError(t, os.MkdirAll(path.Join(tmp, ".git"), 0755))
|
||||||
|
|
||||||
|
_, err := getGitAttributes(context.Background(), tmp, "Dockerfile")
|
||||||
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetGitAttributesNoContext(t *testing.T) {
|
func TestGetGitAttributesNoContext(t *testing.T) {
|
||||||
setupTest(t)
|
setupTest(t)
|
||||||
|
|
||||||
gitattrs := getGitAttributes(context.Background(), "", "Dockerfile")
|
gitattrs, err := getGitAttributes(context.Background(), "", "Dockerfile")
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, gitattrs)
|
assert.Empty(t, gitattrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +63,7 @@ func TestGetGitAttributes(t *testing.T) {
|
|||||||
envGitInfo: "",
|
envGitInfo: "",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"vcs:revision",
|
"vcs:revision",
|
||||||
|
"vcs:source",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -55,6 +78,7 @@ func TestGetGitAttributes(t *testing.T) {
|
|||||||
envGitInfo: "true",
|
envGitInfo: "true",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"vcs:revision",
|
"vcs:revision",
|
||||||
|
"vcs:source",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -64,6 +88,7 @@ func TestGetGitAttributes(t *testing.T) {
|
|||||||
expected: []string{
|
expected: []string{
|
||||||
"label:" + DockerfileLabel,
|
"label:" + DockerfileLabel,
|
||||||
"label:" + specs.AnnotationRevision,
|
"label:" + specs.AnnotationRevision,
|
||||||
|
"label:" + specs.AnnotationSource,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -73,7 +98,9 @@ func TestGetGitAttributes(t *testing.T) {
|
|||||||
expected: []string{
|
expected: []string{
|
||||||
"label:" + DockerfileLabel,
|
"label:" + DockerfileLabel,
|
||||||
"label:" + specs.AnnotationRevision,
|
"label:" + specs.AnnotationRevision,
|
||||||
|
"label:" + specs.AnnotationSource,
|
||||||
"vcs:revision",
|
"vcs:revision",
|
||||||
|
"vcs:source",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -87,52 +114,42 @@ func TestGetGitAttributes(t *testing.T) {
|
|||||||
if tt.envGitInfo != "" {
|
if tt.envGitInfo != "" {
|
||||||
t.Setenv("BUILDX_GIT_INFO", tt.envGitInfo)
|
t.Setenv("BUILDX_GIT_INFO", tt.envGitInfo)
|
||||||
}
|
}
|
||||||
gitattrs := getGitAttributes(context.Background(), ".", "Dockerfile")
|
gitattrs, err := getGitAttributes(context.Background(), ".", "Dockerfile")
|
||||||
|
require.NoError(t, err)
|
||||||
for _, e := range tt.expected {
|
for _, e := range tt.expected {
|
||||||
assert.Contains(t, gitattrs, e)
|
assert.Contains(t, gitattrs, e)
|
||||||
assert.NotEmpty(t, gitattrs[e])
|
assert.NotEmpty(t, gitattrs[e])
|
||||||
if e == "label:"+DockerfileLabel {
|
if e == "label:"+DockerfileLabel {
|
||||||
assert.Equal(t, "Dockerfile", gitattrs[e])
|
assert.Equal(t, "Dockerfile", gitattrs[e])
|
||||||
|
} else if e == "label:"+specs.AnnotationSource || e == "vcs:source" {
|
||||||
|
assert.Equal(t, "git@github.com:docker/buildx.git", gitattrs[e])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetGitAttributesWithRemote(t *testing.T) {
|
|
||||||
setupTest(t)
|
|
||||||
gitutil.GitSetRemote(t, "git@github.com:docker/buildx.git")
|
|
||||||
|
|
||||||
t.Setenv("BUILDX_GIT_LABELS", "true")
|
|
||||||
gitattrs := getGitAttributes(context.Background(), ".", "Dockerfile")
|
|
||||||
assert.Equal(t, 5, len(gitattrs))
|
|
||||||
assert.Contains(t, gitattrs, "label:"+DockerfileLabel)
|
|
||||||
assert.Equal(t, "Dockerfile", gitattrs["label:"+DockerfileLabel])
|
|
||||||
assert.Contains(t, gitattrs, "label:"+specs.AnnotationRevision)
|
|
||||||
assert.NotEmpty(t, gitattrs["label:"+specs.AnnotationRevision])
|
|
||||||
assert.Contains(t, gitattrs, "label:"+specs.AnnotationSource)
|
|
||||||
assert.Equal(t, "git@github.com:docker/buildx.git", gitattrs["label:"+specs.AnnotationSource])
|
|
||||||
assert.Contains(t, gitattrs, "vcs:revision")
|
|
||||||
assert.NotEmpty(t, gitattrs["vcs:revision"])
|
|
||||||
assert.Contains(t, gitattrs, "vcs:source")
|
|
||||||
assert.Equal(t, "git@github.com:docker/buildx.git", gitattrs["vcs:source"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetGitAttributesDirty(t *testing.T) {
|
func TestGetGitAttributesDirty(t *testing.T) {
|
||||||
setupTest(t)
|
setupTest(t)
|
||||||
|
|
||||||
// make a change to test dirty flag
|
// make a change to test dirty flag
|
||||||
df := []byte("FROM alpine:edge\n")
|
df := []byte("FROM alpine:edge\n")
|
||||||
assert.NoError(t, os.Mkdir("dir", 0755))
|
require.NoError(t, os.Mkdir("dir", 0755))
|
||||||
assert.NoError(t, os.WriteFile(filepath.Join("dir", "Dockerfile"), df, 0644))
|
require.NoError(t, os.WriteFile(filepath.Join("dir", "Dockerfile"), df, 0644))
|
||||||
|
|
||||||
t.Setenv("BUILDX_GIT_LABELS", "true")
|
t.Setenv("BUILDX_GIT_LABELS", "true")
|
||||||
gitattrs := getGitAttributes(context.Background(), ".", "Dockerfile")
|
gitattrs, _ := getGitAttributes(context.Background(), ".", "Dockerfile")
|
||||||
assert.Equal(t, 3, len(gitattrs))
|
assert.Equal(t, 5, len(gitattrs))
|
||||||
|
|
||||||
assert.Contains(t, gitattrs, "label:"+DockerfileLabel)
|
assert.Contains(t, gitattrs, "label:"+DockerfileLabel)
|
||||||
assert.Equal(t, "Dockerfile", gitattrs["label:"+DockerfileLabel])
|
assert.Equal(t, "Dockerfile", gitattrs["label:"+DockerfileLabel])
|
||||||
|
assert.Contains(t, gitattrs, "label:"+specs.AnnotationSource)
|
||||||
|
assert.Equal(t, "git@github.com:docker/buildx.git", gitattrs["label:"+specs.AnnotationSource])
|
||||||
assert.Contains(t, gitattrs, "label:"+specs.AnnotationRevision)
|
assert.Contains(t, gitattrs, "label:"+specs.AnnotationRevision)
|
||||||
assert.True(t, strings.HasSuffix(gitattrs["label:"+specs.AnnotationRevision], "-dirty"))
|
assert.True(t, strings.HasSuffix(gitattrs["label:"+specs.AnnotationRevision], "-dirty"))
|
||||||
|
|
||||||
|
assert.Contains(t, gitattrs, "vcs:source")
|
||||||
|
assert.Equal(t, "git@github.com:docker/buildx.git", gitattrs["vcs:source"])
|
||||||
assert.Contains(t, gitattrs, "vcs:revision")
|
assert.Contains(t, gitattrs, "vcs:revision")
|
||||||
assert.True(t, strings.HasSuffix(gitattrs["vcs:revision"], "-dirty"))
|
assert.True(t, strings.HasSuffix(gitattrs["vcs:revision"], "-dirty"))
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -17,6 +17,7 @@ require (
|
|||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
||||||
github.com/hashicorp/hcl/v2 v2.8.2
|
github.com/hashicorp/hcl/v2 v2.8.2
|
||||||
github.com/moby/buildkit v0.11.0-rc1.0.20221213193744-862b22d7e7cf
|
github.com/moby/buildkit v0.11.0-rc1.0.20221213193744-862b22d7e7cf
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2
|
||||||
github.com/morikuni/aec v1.0.0
|
github.com/morikuni/aec v1.0.0
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1
|
github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1
|
||||||
|
2
go.sum
2
go.sum
@ -411,6 +411,7 @@ github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8
|
|||||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||||
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
||||||
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
||||||
@ -780,6 +781,7 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
42
util/gitutil/gitpath_unix.go
Normal file
42
util/gitutil/gitpath_unix.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package gitutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/moby/sys/mountinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func gitPath(wd string) (string, error) {
|
||||||
|
// On WSL2 we need to check if the current working directory is mounted on
|
||||||
|
// a Windows drive and if so, we need to use the Windows git executable.
|
||||||
|
if os.Getenv("WSL_DISTRO_NAME") != "" && wd != "" {
|
||||||
|
// ensure any symlinks are resolved
|
||||||
|
wdPath, err := filepath.EvalSymlinks(wd)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
mi, err := mountinfo.GetMounts(mountinfo.ParentsFilter(wdPath))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// find the longest mount point
|
||||||
|
var idx, maxlen int
|
||||||
|
for i := range mi {
|
||||||
|
if len(mi[i].Mountpoint) > maxlen {
|
||||||
|
maxlen = len(mi[i].Mountpoint)
|
||||||
|
idx = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mi[idx].FSType == "9p" {
|
||||||
|
if p, err := exec.LookPath("git.exe"); err == nil {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exec.LookPath("git")
|
||||||
|
}
|
9
util/gitutil/gitpath_windows.go
Normal file
9
util/gitutil/gitpath_windows.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package gitutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func gitPath(wd string) (string, error) {
|
||||||
|
return exec.LookPath("git.exe")
|
||||||
|
}
|
@ -13,6 +13,7 @@ import (
|
|||||||
type Git struct {
|
type Git struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
wd string
|
wd string
|
||||||
|
gitpath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option provides a variadic option for configuring the git client.
|
// Option provides a variadic option for configuring the git client.
|
||||||
@ -33,14 +34,22 @@ func WithWorkingDir(wd string) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a new git client
|
// New initializes a new git client
|
||||||
func New(opts ...Option) *Git {
|
func New(opts ...Option) (*Git, error) {
|
||||||
|
var err error
|
||||||
c := &Git{
|
c := &Git{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(c)
|
opt(c)
|
||||||
}
|
}
|
||||||
return c
|
|
||||||
|
c.gitpath, err = gitPath(c.wd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("git not found in PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Git) IsInsideWorkTree() bool {
|
func (c *Git) IsInsideWorkTree() bool {
|
||||||
@ -89,16 +98,12 @@ func (c *Git) Tag() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Git) run(args ...string) (string, error) {
|
func (c *Git) run(args ...string) (string, error) {
|
||||||
if _, err := exec.LookPath("git"); err != nil {
|
|
||||||
return "", errors.New("git not present in PATH")
|
|
||||||
}
|
|
||||||
|
|
||||||
var extraArgs = []string{
|
var extraArgs = []string{
|
||||||
"-c", "log.showSignature=false",
|
"-c", "log.showSignature=false",
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append(extraArgs, args...)
|
args = append(extraArgs, args...)
|
||||||
cmd := exec.Command("git", args...)
|
cmd := exec.CommandContext(c.ctx, c.gitpath, args...)
|
||||||
if c.wd != "" {
|
if c.wd != "" {
|
||||||
cmd.Dir = c.wd
|
cmd.Dir = c.wd
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestGit(t *testing.T) {
|
func TestGit(t *testing.T) {
|
||||||
c := New()
|
c, err := New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
out, err := c.run("status")
|
out, err := c.run("status")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, out)
|
require.NotEmpty(t, out)
|
||||||
@ -20,10 +22,12 @@ func TestGit(t *testing.T) {
|
|||||||
|
|
||||||
func TestGitFullCommit(t *testing.T) {
|
func TestGitFullCommit(t *testing.T) {
|
||||||
Mktmp(t)
|
Mktmp(t)
|
||||||
GitInit(t)
|
c, err := New()
|
||||||
GitCommit(t, "bar")
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
GitInit(c, t)
|
||||||
|
GitCommit(c, t, "bar")
|
||||||
|
|
||||||
c := New()
|
|
||||||
out, err := c.FullCommit()
|
out, err := c.FullCommit()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 40, len(out))
|
require.Equal(t, 40, len(out))
|
||||||
@ -31,10 +35,12 @@ func TestGitFullCommit(t *testing.T) {
|
|||||||
|
|
||||||
func TestGitShortCommit(t *testing.T) {
|
func TestGitShortCommit(t *testing.T) {
|
||||||
Mktmp(t)
|
Mktmp(t)
|
||||||
GitInit(t)
|
c, err := New()
|
||||||
GitCommit(t, "bar")
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
GitInit(c, t)
|
||||||
|
GitCommit(c, t, "bar")
|
||||||
|
|
||||||
c := New()
|
|
||||||
out, err := c.ShortCommit()
|
out, err := c.ShortCommit()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 7, len(out))
|
require.Equal(t, 7, len(out))
|
||||||
@ -42,13 +48,15 @@ func TestGitShortCommit(t *testing.T) {
|
|||||||
|
|
||||||
func TestGitTagsPointsAt(t *testing.T) {
|
func TestGitTagsPointsAt(t *testing.T) {
|
||||||
Mktmp(t)
|
Mktmp(t)
|
||||||
GitInit(t)
|
c, err := New()
|
||||||
GitCommit(t, "bar")
|
require.NoError(t, err)
|
||||||
GitTag(t, "v0.8.0")
|
|
||||||
GitCommit(t, "foo")
|
GitInit(c, t)
|
||||||
GitTag(t, "v0.9.0")
|
GitCommit(c, t, "bar")
|
||||||
|
GitTag(c, t, "v0.8.0")
|
||||||
|
GitCommit(c, t, "foo")
|
||||||
|
GitTag(c, t, "v0.9.0")
|
||||||
|
|
||||||
c := New()
|
|
||||||
out, err := c.clean(c.run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate"))
|
out, err := c.clean(c.run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "v0.9.0", out)
|
require.Equal(t, "v0.9.0", out)
|
||||||
@ -56,13 +64,15 @@ func TestGitTagsPointsAt(t *testing.T) {
|
|||||||
|
|
||||||
func TestGitDescribeTags(t *testing.T) {
|
func TestGitDescribeTags(t *testing.T) {
|
||||||
Mktmp(t)
|
Mktmp(t)
|
||||||
GitInit(t)
|
c, err := New()
|
||||||
GitCommit(t, "bar")
|
require.NoError(t, err)
|
||||||
GitTag(t, "v0.8.0")
|
|
||||||
GitCommit(t, "foo")
|
GitInit(c, t)
|
||||||
GitTag(t, "v0.9.0")
|
GitCommit(c, t, "bar")
|
||||||
|
GitTag(c, t, "v0.8.0")
|
||||||
|
GitCommit(c, t, "foo")
|
||||||
|
GitTag(c, t, "v0.9.0")
|
||||||
|
|
||||||
c := New()
|
|
||||||
out, err := c.clean(c.run("describe", "--tags", "--abbrev=0"))
|
out, err := c.clean(c.run("describe", "--tags", "--abbrev=0"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "v0.9.0", out)
|
require.Equal(t, "v0.9.0", out)
|
||||||
|
@ -7,46 +7,46 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GitInit(tb testing.TB) {
|
func GitInit(c *Git, tb testing.TB) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
out, err := fakeGit("init")
|
out, err := fakeGit(c, "init")
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
require.Contains(tb, out, "Initialized empty Git repository")
|
require.Contains(tb, out, "Initialized empty Git repository")
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
GitCheckoutBranch(tb, "main")
|
GitCheckoutBranch(c, tb, "main")
|
||||||
_, _ = fakeGit("branch", "-D", "master")
|
_, _ = fakeGit(c, "branch", "-D", "master")
|
||||||
}
|
}
|
||||||
|
|
||||||
func GitCommit(tb testing.TB, msg string) {
|
func GitCommit(c *Git, tb testing.TB, msg string) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
out, err := fakeGit("commit", "--allow-empty", "-m", msg)
|
out, err := fakeGit(c, "commit", "--allow-empty", "-m", msg)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
require.Contains(tb, out, "main", msg)
|
require.Contains(tb, out, "main", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GitTag(tb testing.TB, tag string) {
|
func GitTag(c *Git, tb testing.TB, tag string) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
out, err := fakeGit("tag", tag)
|
out, err := fakeGit(c, "tag", tag)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
require.Empty(tb, out)
|
require.Empty(tb, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GitCheckoutBranch(tb testing.TB, name string) {
|
func GitCheckoutBranch(c *Git, tb testing.TB, name string) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
out, err := fakeGit("checkout", "-b", name)
|
out, err := fakeGit(c, "checkout", "-b", name)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
require.Empty(tb, out)
|
require.Empty(tb, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GitAdd(tb testing.TB, file string) {
|
func GitAdd(c *Git, tb testing.TB, file string) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
_, err := fakeGit("add", file)
|
_, err := fakeGit(c, "add", file)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GitSetRemote(tb testing.TB, url string) {
|
func GitSetRemote(c *Git, tb testing.TB, url string) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
_, err := fakeGit("remote", "add", "origin", url)
|
_, err := fakeGit(c, "remote", "add", "origin", url)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ func Mktmp(tb testing.TB) string {
|
|||||||
return folder
|
return folder
|
||||||
}
|
}
|
||||||
|
|
||||||
func fakeGit(args ...string) (string, error) {
|
func fakeGit(c *Git, args ...string) (string, error) {
|
||||||
allArgs := []string{
|
allArgs := []string{
|
||||||
"-c", "user.name=buildx",
|
"-c", "user.name=buildx",
|
||||||
"-c", "user.email=buildx@docker.com",
|
"-c", "user.email=buildx@docker.com",
|
||||||
@ -71,6 +71,5 @@ func fakeGit(args ...string) (string, error) {
|
|||||||
"-c", "log.showSignature=false",
|
"-c", "log.showSignature=false",
|
||||||
}
|
}
|
||||||
allArgs = append(allArgs, args...)
|
allArgs = append(allArgs, args...)
|
||||||
c := New()
|
|
||||||
return c.clean(c.run(allArgs...))
|
return c.clean(c.run(allArgs...))
|
||||||
}
|
}
|
||||||
|
202
vendor/github.com/moby/sys/mountinfo/LICENSE
generated
vendored
Normal file
202
vendor/github.com/moby/sys/mountinfo/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
44
vendor/github.com/moby/sys/mountinfo/doc.go
generated
vendored
Normal file
44
vendor/github.com/moby/sys/mountinfo/doc.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Package mountinfo provides a set of functions to retrieve information about OS mounts.
|
||||||
|
//
|
||||||
|
// Currently it supports Linux. For historical reasons, there is also some support for FreeBSD and OpenBSD,
|
||||||
|
// and a shallow implementation for Windows, but in general this is Linux-only package, so
|
||||||
|
// the rest of the document only applies to Linux, unless explicitly specified otherwise.
|
||||||
|
//
|
||||||
|
// In Linux, information about mounts seen by the current process is available from
|
||||||
|
// /proc/self/mountinfo. Note that due to mount namespaces, different processes can
|
||||||
|
// see different mounts. A per-process mountinfo table is available from /proc/<PID>/mountinfo,
|
||||||
|
// where <PID> is a numerical process identifier.
|
||||||
|
//
|
||||||
|
// In general, /proc is not a very efficient interface, and mountinfo is not an exception.
|
||||||
|
// For example, there is no way to get information about a specific mount point (i.e. it
|
||||||
|
// is all-or-nothing). This package tries to hide the /proc ineffectiveness by using
|
||||||
|
// parse filters while reading mountinfo. A filter can skip some entries, or stop
|
||||||
|
// processing the rest of the file once the needed information is found.
|
||||||
|
//
|
||||||
|
// For mountinfo filters that accept path as an argument, the path must be absolute,
|
||||||
|
// having all symlinks resolved, and being cleaned (i.e. no extra slashes or dots).
|
||||||
|
// One way to achieve all of the above is to employ filepath.Abs followed by
|
||||||
|
// filepath.EvalSymlinks (the latter calls filepath.Clean on the result so
|
||||||
|
// there is no need to explicitly call filepath.Clean).
|
||||||
|
//
|
||||||
|
// NOTE that in many cases there is no need to consult mountinfo at all. Here are some
|
||||||
|
// of the cases where mountinfo should not be parsed:
|
||||||
|
//
|
||||||
|
// 1. Before performing a mount. Usually, this is not needed, but if required (say to
|
||||||
|
// prevent over-mounts), to check whether a directory is mounted, call os.Lstat
|
||||||
|
// on it and its parent directory, and compare their st.Sys().(*syscall.Stat_t).Dev
|
||||||
|
// fields -- if they differ, then the directory is the mount point. NOTE this does
|
||||||
|
// not work for bind mounts. Optionally, the filesystem type can also be checked
|
||||||
|
// by calling unix.Statfs and checking the Type field (i.e. filesystem type).
|
||||||
|
//
|
||||||
|
// 2. After performing a mount. If there is no error returned, the mount succeeded;
|
||||||
|
// checking the mount table for a new mount is redundant and expensive.
|
||||||
|
//
|
||||||
|
// 3. Before performing an unmount. It is more efficient to do an unmount and ignore
|
||||||
|
// a specific error (EINVAL) which tells the directory is not mounted.
|
||||||
|
//
|
||||||
|
// 4. After performing an unmount. If there is no error returned, the unmount succeeded.
|
||||||
|
//
|
||||||
|
// 5. To find the mount point root of a specific directory. You can perform os.Stat()
|
||||||
|
// on the directory and traverse up until the Dev field of a parent directory differs.
|
||||||
|
package mountinfo
|
101
vendor/github.com/moby/sys/mountinfo/mounted_linux.go
generated
vendored
Normal file
101
vendor/github.com/moby/sys/mountinfo/mounted_linux.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MountedFast is a method of detecting a mount point without reading
|
||||||
|
// mountinfo from procfs. A caller can only trust the result if no error
|
||||||
|
// and sure == true are returned. Otherwise, other methods (e.g. parsing
|
||||||
|
// /proc/mounts) have to be used. If unsure, use Mounted instead (which
|
||||||
|
// uses MountedFast, but falls back to parsing mountinfo if needed).
|
||||||
|
//
|
||||||
|
// If a non-existent path is specified, an appropriate error is returned.
|
||||||
|
// In case the caller is not interested in this particular error, it should
|
||||||
|
// be handled separately using e.g. errors.Is(err, fs.ErrNotExist).
|
||||||
|
//
|
||||||
|
// This function is only available on Linux. When available (since kernel
|
||||||
|
// v5.6), openat2(2) syscall is used to reliably detect all mounts. Otherwise,
|
||||||
|
// the implementation falls back to using stat(2), which can reliably detect
|
||||||
|
// normal (but not bind) mounts.
|
||||||
|
func MountedFast(path string) (mounted, sure bool, err error) {
|
||||||
|
// Root is always mounted.
|
||||||
|
if path == string(os.PathSeparator) {
|
||||||
|
return true, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err = normalizePath(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
|
mounted, sure, err = mountedFast(path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// mountedByOpenat2 is a method of detecting a mount that works for all kinds
|
||||||
|
// of mounts (incl. bind mounts), but requires a recent (v5.6+) linux kernel.
|
||||||
|
func mountedByOpenat2(path string) (bool, error) {
|
||||||
|
dir, last := filepath.Split(path)
|
||||||
|
|
||||||
|
dirfd, err := unix.Openat2(unix.AT_FDCWD, dir, &unix.OpenHow{
|
||||||
|
Flags: unix.O_PATH | unix.O_CLOEXEC,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, &os.PathError{Op: "openat2", Path: dir, Err: err}
|
||||||
|
}
|
||||||
|
fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{
|
||||||
|
Flags: unix.O_PATH | unix.O_CLOEXEC | unix.O_NOFOLLOW,
|
||||||
|
Resolve: unix.RESOLVE_NO_XDEV,
|
||||||
|
})
|
||||||
|
_ = unix.Close(dirfd)
|
||||||
|
switch err { //nolint:errorlint // unix errors are bare
|
||||||
|
case nil: // definitely not a mount
|
||||||
|
_ = unix.Close(fd)
|
||||||
|
return false, nil
|
||||||
|
case unix.EXDEV: // definitely a mount
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
// not sure
|
||||||
|
return false, &os.PathError{Op: "openat2", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mountedFast is similar to MountedFast, except it expects a normalized path.
|
||||||
|
func mountedFast(path string) (mounted, sure bool, err error) {
|
||||||
|
// Root is always mounted.
|
||||||
|
if path == string(os.PathSeparator) {
|
||||||
|
return true, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try a fast path, using openat2() with RESOLVE_NO_XDEV.
|
||||||
|
mounted, err = mountedByOpenat2(path)
|
||||||
|
if err == nil {
|
||||||
|
return mounted, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another fast path: compare st.st_dev fields.
|
||||||
|
mounted, err = mountedByStat(path)
|
||||||
|
// This does not work for bind mounts, so false negative
|
||||||
|
// is possible, therefore only trust if return is true.
|
||||||
|
if mounted && err == nil {
|
||||||
|
return true, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mounted(path string) (bool, error) {
|
||||||
|
path, err := normalizePath(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
mounted, sure, err := mountedFast(path)
|
||||||
|
if sure && err == nil {
|
||||||
|
return mounted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to parsing mountinfo.
|
||||||
|
return mountedByMountinfo(path)
|
||||||
|
}
|
53
vendor/github.com/moby/sys/mountinfo/mounted_unix.go
generated
vendored
Normal file
53
vendor/github.com/moby/sys/mountinfo/mounted_unix.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
//go:build linux || freebsd || openbsd || darwin
|
||||||
|
// +build linux freebsd openbsd darwin
|
||||||
|
|
||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mountedByStat(path string) (bool, error) {
|
||||||
|
var st unix.Stat_t
|
||||||
|
|
||||||
|
if err := unix.Lstat(path, &st); err != nil {
|
||||||
|
return false, &os.PathError{Op: "stat", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
dev := st.Dev
|
||||||
|
parent := filepath.Dir(path)
|
||||||
|
if err := unix.Lstat(parent, &st); err != nil {
|
||||||
|
return false, &os.PathError{Op: "stat", Path: parent, Err: err}
|
||||||
|
}
|
||||||
|
if dev != st.Dev {
|
||||||
|
// Device differs from that of parent,
|
||||||
|
// so definitely a mount point.
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
// NB: this does not detect bind mounts on Linux.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizePath(path string) (realPath string, err error) {
|
||||||
|
if realPath, err = filepath.Abs(path); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(realPath); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return realPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mountedByMountinfo(path string) (bool, error) {
|
||||||
|
entries, err := GetMounts(SingleEntryFilter(path))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(entries) > 0, nil
|
||||||
|
}
|
67
vendor/github.com/moby/sys/mountinfo/mountinfo.go
generated
vendored
Normal file
67
vendor/github.com/moby/sys/mountinfo/mountinfo.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMounts retrieves a list of mounts for the current running process,
|
||||||
|
// with an optional filter applied (use nil for no filter).
|
||||||
|
func GetMounts(f FilterFunc) ([]*Info, error) {
|
||||||
|
return parseMountTable(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounted determines if a specified path is a mount point. In case of any
|
||||||
|
// error, false (and an error) is returned.
|
||||||
|
//
|
||||||
|
// If a non-existent path is specified, an appropriate error is returned.
|
||||||
|
// In case the caller is not interested in this particular error, it should
|
||||||
|
// be handled separately using e.g. errors.Is(err, fs.ErrNotExist).
|
||||||
|
func Mounted(path string) (bool, error) {
|
||||||
|
// root is always mounted
|
||||||
|
if path == string(os.PathSeparator) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return mounted(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info reveals information about a particular mounted filesystem. This
|
||||||
|
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
||||||
|
type Info struct {
|
||||||
|
// ID is a unique identifier of the mount (may be reused after umount).
|
||||||
|
ID int
|
||||||
|
|
||||||
|
// Parent is the ID of the parent mount (or of self for the root
|
||||||
|
// of this mount namespace's mount tree).
|
||||||
|
Parent int
|
||||||
|
|
||||||
|
// Major and Minor are the major and the minor components of the Dev
|
||||||
|
// field of unix.Stat_t structure returned by unix.*Stat calls for
|
||||||
|
// files on this filesystem.
|
||||||
|
Major, Minor int
|
||||||
|
|
||||||
|
// Root is the pathname of the directory in the filesystem which forms
|
||||||
|
// the root of this mount.
|
||||||
|
Root string
|
||||||
|
|
||||||
|
// Mountpoint is the pathname of the mount point relative to the
|
||||||
|
// process's root directory.
|
||||||
|
Mountpoint string
|
||||||
|
|
||||||
|
// Options is a comma-separated list of mount options.
|
||||||
|
Options string
|
||||||
|
|
||||||
|
// Optional are zero or more fields of the form "tag[:value]",
|
||||||
|
// separated by a space. Currently, the possible optional fields are
|
||||||
|
// "shared", "master", "propagate_from", and "unbindable". For more
|
||||||
|
// information, see mount_namespaces(7) Linux man page.
|
||||||
|
Optional string
|
||||||
|
|
||||||
|
// FSType is the filesystem type in the form "type[.subtype]".
|
||||||
|
FSType string
|
||||||
|
|
||||||
|
// Source is filesystem-specific information, or "none".
|
||||||
|
Source string
|
||||||
|
|
||||||
|
// VFSOptions is a comma-separated list of superblock options.
|
||||||
|
VFSOptions string
|
||||||
|
}
|
56
vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go
generated
vendored
Normal file
56
vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//go:build freebsd || openbsd || darwin
|
||||||
|
// +build freebsd openbsd darwin
|
||||||
|
|
||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
// parseMountTable returns information about mounted filesystems
|
||||||
|
func parseMountTable(filter FilterFunc) ([]*Info, error) {
|
||||||
|
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := make([]unix.Statfs_t, count)
|
||||||
|
_, err = unix.Getfsstat(entries, unix.MNT_WAIT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []*Info
|
||||||
|
for _, entry := range entries {
|
||||||
|
var skip, stop bool
|
||||||
|
mountinfo := getMountinfo(&entry)
|
||||||
|
|
||||||
|
if filter != nil {
|
||||||
|
// filter out entries we're not interested in
|
||||||
|
skip, stop = filter(mountinfo)
|
||||||
|
if skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, mountinfo)
|
||||||
|
if stop {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mounted(path string) (bool, error) {
|
||||||
|
path, err := normalizePath(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// Fast path: compare st.st_dev fields.
|
||||||
|
// This should always work for FreeBSD and OpenBSD.
|
||||||
|
mounted, err := mountedByStat(path)
|
||||||
|
if err == nil {
|
||||||
|
return mounted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to parsing mountinfo
|
||||||
|
return mountedByMountinfo(path)
|
||||||
|
}
|
63
vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go
generated
vendored
Normal file
63
vendor/github.com/moby/sys/mountinfo/mountinfo_filters.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// FilterFunc is a type defining a callback function for GetMount(),
|
||||||
|
// used to filter out mountinfo entries we're not interested in,
|
||||||
|
// and/or stop further processing if we found what we wanted.
|
||||||
|
//
|
||||||
|
// It takes a pointer to the Info struct (fully populated with all available
|
||||||
|
// fields on the GOOS platform), and returns two booleans:
|
||||||
|
//
|
||||||
|
// skip: true if the entry should be skipped;
|
||||||
|
//
|
||||||
|
// stop: true if parsing should be stopped after the entry.
|
||||||
|
type FilterFunc func(*Info) (skip, stop bool)
|
||||||
|
|
||||||
|
// PrefixFilter discards all entries whose mount points do not start with, or
|
||||||
|
// are equal to the path specified in prefix. The prefix path must be absolute,
|
||||||
|
// have all symlinks resolved, and cleaned (i.e. no extra slashes or dots).
|
||||||
|
//
|
||||||
|
// PrefixFilter treats prefix as a path, not a partial prefix, which means that
|
||||||
|
// given "/foo", "/foo/bar" and "/foobar" entries, PrefixFilter("/foo") returns
|
||||||
|
// "/foo" and "/foo/bar", and discards "/foobar".
|
||||||
|
func PrefixFilter(prefix string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/")
|
||||||
|
return skip, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SingleEntryFilter looks for a specific entry.
|
||||||
|
func SingleEntryFilter(mp string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
if m.Mountpoint == mp {
|
||||||
|
return false, true // don't skip, stop now
|
||||||
|
}
|
||||||
|
return true, false // skip, keep going
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsFilter returns all entries whose mount points
|
||||||
|
// can be parents of a path specified, discarding others.
|
||||||
|
//
|
||||||
|
// For example, given /var/lib/docker/something, entries
|
||||||
|
// like /var/lib/docker, /var and / are returned.
|
||||||
|
func ParentsFilter(path string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
skip := !strings.HasPrefix(path, m.Mountpoint)
|
||||||
|
return skip, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FSTypeFilter returns all entries that match provided fstype(s).
|
||||||
|
func FSTypeFilter(fstype ...string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
for _, t := range fstype {
|
||||||
|
if m.FSType == t {
|
||||||
|
return false, false // don't skip, keep going
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, false // skip, keep going
|
||||||
|
}
|
||||||
|
}
|
14
vendor/github.com/moby/sys/mountinfo/mountinfo_freebsdlike.go
generated
vendored
Normal file
14
vendor/github.com/moby/sys/mountinfo/mountinfo_freebsdlike.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//go:build freebsd || darwin
|
||||||
|
// +build freebsd darwin
|
||||||
|
|
||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
func getMountinfo(entry *unix.Statfs_t) *Info {
|
||||||
|
return &Info{
|
||||||
|
Mountpoint: unix.ByteSliceToString(entry.Mntonname[:]),
|
||||||
|
FSType: unix.ByteSliceToString(entry.Fstypename[:]),
|
||||||
|
Source: unix.ByteSliceToString(entry.Mntfromname[:]),
|
||||||
|
}
|
||||||
|
}
|
214
vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go
generated
vendored
Normal file
214
vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMountsFromReader retrieves a list of mounts from the
|
||||||
|
// reader provided, with an optional filter applied (use nil
|
||||||
|
// for no filter). This can be useful in tests or benchmarks
|
||||||
|
// that provide fake mountinfo data, or when a source other
|
||||||
|
// than /proc/self/mountinfo needs to be read from.
|
||||||
|
//
|
||||||
|
// This function is Linux-specific.
|
||||||
|
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
|
||||||
|
s := bufio.NewScanner(r)
|
||||||
|
out := []*Info{}
|
||||||
|
for s.Scan() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
/*
|
||||||
|
See http://man7.org/linux/man-pages/man5/proc.5.html
|
||||||
|
|
||||||
|
36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||||
|
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
|
||||||
|
|
||||||
|
(1) mount ID: unique identifier of the mount (may be reused after umount)
|
||||||
|
(2) parent ID: ID of parent (or of self for the top of the mount tree)
|
||||||
|
(3) major:minor: value of st_dev for files on filesystem
|
||||||
|
(4) root: root of the mount within the filesystem
|
||||||
|
(5) mount point: mount point relative to the process's root
|
||||||
|
(6) mount options: per mount options
|
||||||
|
(7) optional fields: zero or more fields of the form "tag[:value]"
|
||||||
|
(8) separator: marks the end of the optional fields
|
||||||
|
(9) filesystem type: name of filesystem of the form "type[.subtype]"
|
||||||
|
(10) mount source: filesystem specific information or "none"
|
||||||
|
(11) super options: per super block options
|
||||||
|
|
||||||
|
In other words, we have:
|
||||||
|
* 6 mandatory fields (1)..(6)
|
||||||
|
* 0 or more optional fields (7)
|
||||||
|
* a separator field (8)
|
||||||
|
* 3 mandatory fields (9)..(11)
|
||||||
|
*/
|
||||||
|
|
||||||
|
text := s.Text()
|
||||||
|
fields := strings.Split(text, " ")
|
||||||
|
numFields := len(fields)
|
||||||
|
if numFields < 10 {
|
||||||
|
// should be at least 10 fields
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// separator field
|
||||||
|
sepIdx := numFields - 4
|
||||||
|
// In Linux <= 3.9 mounting a cifs with spaces in a share
|
||||||
|
// name (like "//srv/My Docs") _may_ end up having a space
|
||||||
|
// in the last field of mountinfo (like "unc=//serv/My Docs").
|
||||||
|
// Since kernel 3.10-rc1, cifs option "unc=" is ignored,
|
||||||
|
// so spaces should not appear.
|
||||||
|
//
|
||||||
|
// Check for a separator, and work around the spaces bug
|
||||||
|
for fields[sepIdx] != "-" {
|
||||||
|
sepIdx--
|
||||||
|
if sepIdx == 5 {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Info{}
|
||||||
|
|
||||||
|
p.Mountpoint, err = unescape(fields[4])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err)
|
||||||
|
}
|
||||||
|
p.FSType, err = unescape(fields[sepIdx+1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: fstype: %w", fields[sepIdx+1], err)
|
||||||
|
}
|
||||||
|
p.Source, err = unescape(fields[sepIdx+2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: source: %w", fields[sepIdx+2], err)
|
||||||
|
}
|
||||||
|
p.VFSOptions = fields[sepIdx+3]
|
||||||
|
|
||||||
|
// ignore any numbers parsing errors, as there should not be any
|
||||||
|
p.ID, _ = strconv.Atoi(fields[0])
|
||||||
|
p.Parent, _ = strconv.Atoi(fields[1])
|
||||||
|
mm := strings.SplitN(fields[2], ":", 3)
|
||||||
|
if len(mm) != 2 {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm)
|
||||||
|
}
|
||||||
|
p.Major, _ = strconv.Atoi(mm[0])
|
||||||
|
p.Minor, _ = strconv.Atoi(mm[1])
|
||||||
|
|
||||||
|
p.Root, err = unescape(fields[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Options = fields[5]
|
||||||
|
|
||||||
|
// zero or more optional fields
|
||||||
|
p.Optional = strings.Join(fields[6:sepIdx], " ")
|
||||||
|
|
||||||
|
// Run the filter after parsing all fields.
|
||||||
|
var skip, stop bool
|
||||||
|
if filter != nil {
|
||||||
|
skip, stop = filter(p)
|
||||||
|
if skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, p)
|
||||||
|
if stop {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMountTable(filter FilterFunc) ([]*Info, error) {
|
||||||
|
f, err := os.Open("/proc/self/mountinfo")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return GetMountsFromReader(f, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidMountInfo retrieves the list of mounts from a given process' mount
|
||||||
|
// namespace. Unless there is a need to get mounts from a mount namespace
|
||||||
|
// different from that of a calling process, use GetMounts.
|
||||||
|
//
|
||||||
|
// This function is Linux-specific.
|
||||||
|
//
|
||||||
|
// Deprecated: this will be removed before v1; use GetMountsFromReader with
|
||||||
|
// opened /proc/<pid>/mountinfo as an argument instead.
|
||||||
|
func PidMountInfo(pid int) ([]*Info, error) {
|
||||||
|
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return GetMountsFromReader(f, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A few specific characters in mountinfo path entries (root and mountpoint)
|
||||||
|
// are escaped using a backslash followed by a character's ascii code in octal.
|
||||||
|
//
|
||||||
|
// space -- as \040
|
||||||
|
// tab (aka \t) -- as \011
|
||||||
|
// newline (aka \n) -- as \012
|
||||||
|
// backslash (aka \\) -- as \134
|
||||||
|
//
|
||||||
|
// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
|
||||||
|
func unescape(path string) (string, error) {
|
||||||
|
// try to avoid copying
|
||||||
|
if strings.IndexByte(path, '\\') == -1 {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following code is UTF-8 transparent as it only looks for some
|
||||||
|
// specific characters (backslash and 0..7) with values < utf8.RuneSelf,
|
||||||
|
// and everything else is passed through as is.
|
||||||
|
buf := make([]byte, len(path))
|
||||||
|
bufLen := 0
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
if path[i] != '\\' {
|
||||||
|
buf[bufLen] = path[i]
|
||||||
|
bufLen++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s := path[i:]
|
||||||
|
if len(s) < 4 {
|
||||||
|
// too short
|
||||||
|
return "", fmt.Errorf("bad escape sequence %q: too short", s)
|
||||||
|
}
|
||||||
|
c := s[1]
|
||||||
|
switch c {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
|
v := c - '0'
|
||||||
|
for j := 2; j < 4; j++ { // one digit already; two more
|
||||||
|
if s[j] < '0' || s[j] > '7' {
|
||||||
|
return "", fmt.Errorf("bad escape sequence %q: not a digit", s[:3])
|
||||||
|
}
|
||||||
|
x := s[j] - '0'
|
||||||
|
v = (v << 3) | x
|
||||||
|
}
|
||||||
|
if v > 255 {
|
||||||
|
return "", fmt.Errorf("bad escape sequence %q: out of range" + s[:3])
|
||||||
|
}
|
||||||
|
buf[bufLen] = v
|
||||||
|
bufLen++
|
||||||
|
i += 3
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("bad escape sequence %q: not a digit" + s[:3])
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf[:bufLen]), nil
|
||||||
|
}
|
11
vendor/github.com/moby/sys/mountinfo/mountinfo_openbsd.go
generated
vendored
Normal file
11
vendor/github.com/moby/sys/mountinfo/mountinfo_openbsd.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
func getMountinfo(entry *unix.Statfs_t) *Info {
|
||||||
|
return &Info{
|
||||||
|
Mountpoint: unix.ByteSliceToString(entry.F_mntonname[:]),
|
||||||
|
FSType: unix.ByteSliceToString(entry.F_fstypename[:]),
|
||||||
|
Source: unix.ByteSliceToString(entry.F_mntfromname[:]),
|
||||||
|
}
|
||||||
|
}
|
19
vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go
generated
vendored
Normal file
19
vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//go:build !windows && !linux && !freebsd && !openbsd && !darwin
|
||||||
|
// +build !windows,!linux,!freebsd,!openbsd,!darwin
|
||||||
|
|
||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNotImplemented = fmt.Errorf("not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
|
||||||
|
func parseMountTable(_ FilterFunc) ([]*Info, error) {
|
||||||
|
return nil, errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func mounted(path string) (bool, error) {
|
||||||
|
return false, errNotImplemented
|
||||||
|
}
|
10
vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go
generated
vendored
Normal file
10
vendor/github.com/moby/sys/mountinfo/mountinfo_windows.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
func parseMountTable(_ FilterFunc) ([]*Info, error) {
|
||||||
|
// Do NOT return an error!
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mounted(_ string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@ -510,6 +510,9 @@ github.com/moby/patternmatcher
|
|||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/moby/spdystream
|
github.com/moby/spdystream
|
||||||
github.com/moby/spdystream/spdy
|
github.com/moby/spdystream/spdy
|
||||||
|
# github.com/moby/sys/mountinfo v0.6.2
|
||||||
|
## explicit; go 1.16
|
||||||
|
github.com/moby/sys/mountinfo
|
||||||
# github.com/moby/sys/sequential v0.5.0
|
# github.com/moby/sys/sequential v0.5.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/moby/sys/sequential
|
github.com/moby/sys/sequential
|
||||||
|
Loading…
x
Reference in New Issue
Block a user