From c574e03d985fde28c9f6ea17dd2d38a70b7152a1 Mon Sep 17 00:00:00 2001 From: Youfu Zhang <1315097+zhangyoufu@users.noreply.github.com> Date: Thu, 16 Jul 2026 06:05:25 +0000 Subject: [PATCH 1/2] vendor github.com/moby/api/api changes --- cmd/docker-trust/go.mod | 2 ++ vendor.mod | 2 ++ vendor.sum | 4 ++-- vendor/github.com/moby/moby/api/types/container/hostconfig.go | 1 + vendor/modules.txt | 3 ++- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/docker-trust/go.mod b/cmd/docker-trust/go.mod index e29e424805e8..76123a95d907 100644 --- a/cmd/docker-trust/go.mod +++ b/cmd/docker-trust/go.mod @@ -75,3 +75,5 @@ require ( google.golang.org/grpc v1.80.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) + +replace github.com/moby/moby/api => github.com/zhangyoufu/moby/api v0.0.0-20260716054254-a5f8a6c9858d diff --git a/vendor.mod b/vendor.mod index 832b84b4a5f7..4ae5c1503b4d 100644 --- a/vendor.mod +++ b/vendor.mod @@ -111,3 +111,5 @@ require ( google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect ) + +replace github.com/moby/moby/api => github.com/zhangyoufu/moby/api v0.0.0-20260716054254-a5f8a6c9858d diff --git a/vendor.sum b/vendor.sum index 6e0a010bd05e..cb2e9195c0c0 100644 --- a/vendor.sum +++ b/vendor.sum @@ -113,8 +113,6 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc= -github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= github.com/moby/moby/client v0.5.0 h1:5XhyPk2fuOWf6RlSFa3MkIIgDZkF25xToXW8Q/BH7cc= github.com/moby/moby/client v0.5.0/go.mod h1:rcVpF8ncl9vo5gaIBdol6CnbEtSj1uxMvEV/UrykF/s= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= @@ -202,6 +200,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zhangyoufu/moby/api v0.0.0-20260716054254-a5f8a6c9858d h1:lZGeAaZ0K/yk6xu2U8INf7oz7Zopu/n0onCXFeFunLM= +github.com/zhangyoufu/moby/api v0.0.0-20260716054254-a5f8a6c9858d/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ= go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= diff --git a/vendor/github.com/moby/moby/api/types/container/hostconfig.go b/vendor/github.com/moby/moby/api/types/container/hostconfig.go index 0f889c65124c..b9b6256e320e 100644 --- a/vendor/github.com/moby/moby/api/types/container/hostconfig.go +++ b/vendor/github.com/moby/moby/api/types/container/hostconfig.go @@ -454,6 +454,7 @@ type HostConfig struct { ShmSize int64 // Total shm memory usage Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container Runtime string `json:",omitempty"` // Runtime to use with this container + Umask *uint32 `json:",omitempty"` // Umask to use for the container // Applicable to Windows Isolation Isolation // Isolation technology of the container (e.g. default, hyperv) diff --git a/vendor/modules.txt b/vendor/modules.txt index c924f430f753..f7927134376e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -167,7 +167,7 @@ github.com/moby/docker-image-spec/specs-go/v1 github.com/moby/go-archive github.com/moby/go-archive/compression github.com/moby/go-archive/tarheader -# github.com/moby/moby/api v1.55.0 +# github.com/moby/moby/api v1.55.0 => github.com/zhangyoufu/moby/api v0.0.0-20260716054254-a5f8a6c9858d ## explicit; go 1.24 github.com/moby/moby/api/pkg/authconfig github.com/moby/moby/api/pkg/stdcopy @@ -565,3 +565,4 @@ gotest.tools/v3/skip # tags.cncf.io/container-device-interface v1.1.0 ## explicit; go 1.21 tags.cncf.io/container-device-interface/pkg/parser +# github.com/moby/moby/api => github.com/zhangyoufu/moby/api v0.0.0-20260716054254-a5f8a6c9858d From 47f19b6288092b9d70253ee279d31dfc67a1f2e1 Mon Sep 17 00:00:00 2001 From: Youfu Zhang <1315097+zhangyoufu@users.noreply.github.com> Date: Thu, 16 Jul 2026 07:59:14 +0000 Subject: [PATCH 2/2] cli/command/container: add create/run --umask Signed-off-by: Youfu Zhang --- cli/command/container/opts.go | 3 ++ cli/command/container/opts_test.go | 13 +++++++ e2e/container/run_test.go | 25 +++++++++++++ opts/opts.go | 37 +++++++++++++++++++ opts/opts_test.go | 58 ++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 1af0b64546a4..1a85170de25e 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -139,6 +139,7 @@ type containerOptions struct { runtime string autoRemove bool init bool + umask opts.UmaskOpt annotations *opts.MapOpts Image string @@ -313,6 +314,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions { flags.Var(&copts.shmSize, "shm-size", "Size of /dev/shm") flags.StringVar(&copts.utsMode, "uts", "", "UTS namespace to use") flags.StringVar(&copts.runtime, "runtime", "", "Runtime to use for this container") + flags.Var(&copts.umask, "umask", "Set the umask for the container") flags.BoolVar(&copts.init, "init", false, "Run an init inside the container that forwards signals and reaps processes") flags.SetAnnotation("init", "version", []string{"1.25"}) @@ -710,6 +712,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con MaskedPaths: maskedPaths, ReadonlyPaths: readonlyPaths, Annotations: copts.annotations.GetAll(), + Umask: copts.umask.Value(), } if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() { diff --git a/cli/command/container/opts_test.go b/cli/command/container/opts_test.go index 0781e99687c0..556e013066fa 100644 --- a/cli/command/container/opts_test.go +++ b/cli/command/container/opts_test.go @@ -154,6 +154,19 @@ func TestParseRunLinks(t *testing.T) { } } +func TestParseRunWithoutUmask(t *testing.T) { + _, hostConfig, _, err := parseRun([]string{"ubuntu", "bash"}) + assert.NilError(t, err) + assert.Assert(t, hostConfig.Umask == nil) +} + +func TestParseRunUmask(t *testing.T) { + _, hostConfig, _, err := parseRun([]string{"--umask", "0022", "ubuntu", "bash"}) + assert.NilError(t, err) + assert.Assert(t, hostConfig.Umask != nil) + assert.Equal(t, uint32(0o22), *hostConfig.Umask) +} + func TestParseRunAttach(t *testing.T) { tests := []struct { input string diff --git a/e2e/container/run_test.go b/e2e/container/run_test.go index 6b35127b47a3..5fa2a30cb227 100644 --- a/e2e/container/run_test.go +++ b/e2e/container/run_test.go @@ -110,6 +110,31 @@ func TestRunWithCgroupNamespace(t *testing.T) { result.Assert(t, icmd.Success) } +func TestRunUmask(t *testing.T) { + environment.SkipIfDaemonNotLinux(t) + + testCases := []struct { + name string + args []string + expected string + }{ + {name: "unset", expected: "0022\n"}, + {name: "zero", args: []string{"--umask", "0"}, expected: "0000\n"}, + {name: "octal-022", args: []string{"--umask", "022"}, expected: "0022\n"}, + {name: "octal-777", args: []string{"--umask", "777"}, expected: "0777\n"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + args := []string{"run", "--rm", fixtures.AlpineImage, "sh", "-c", "umask"} + args = append(args[:2], append(tc.args, args[2:]...)...) + result := icmd.RunCommand("docker", args...) + result.Assert(t, icmd.Success) + assert.Equal(t, result.Stdout(), tc.expected) + }) + } +} + func TestMountSubvolume(t *testing.T) { skip.If(t, versions.LessThan(environment.DaemonAPIVersion(t), "1.45")) volName := "test-volume-" + t.Name() diff --git a/opts/opts.go b/opts/opts.go index 2e0adac6e0db..45d082f1f9c3 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -11,6 +11,7 @@ import ( "net" "path" "slices" + "strconv" "strings" "github.com/docker/cli/internal/lazyregexp" @@ -477,3 +478,39 @@ func (m *MemSwapBytes) UnmarshalJSON(s []byte) error { b := MemBytes(*m) return b.UnmarshalJSON(s) } + +// UmaskOpt is a type for umask values in octal format +type UmaskOpt struct { + ptr *uint32 +} + +// Set sets the value of the UmaskOpt by passing a string in octal format +func (u *UmaskOpt) Set(s string) error { + v, err := strconv.ParseUint(s, 8, 32) + if err != nil { + return err + } + if u.ptr == nil { + u.ptr = new(uint32) + } + *u.ptr = uint32(v) + return nil +} + +// Type returns the type +func (*UmaskOpt) Type() string { + return "umask" +} + +// Value returns the uint32 ptr +func (u *UmaskOpt) Value() *uint32 { + return u.ptr +} + +// String returns the umask value in octal format, or "(unset)" if the pointer is nil. +func (u *UmaskOpt) String() string { + if u.ptr == nil { + return "(unset)" + } + return fmt.Sprintf("%#04o", uint64(*u.ptr)) +} diff --git a/opts/opts_test.go b/opts/opts_test.go index e978c2ea8799..233e8132d7f3 100644 --- a/opts/opts_test.go +++ b/opts/opts_test.go @@ -428,3 +428,61 @@ func TestParseCPUsReturnZeroOnInvalidValues(t *testing.T) { resValue, _ = ParseCPUs("1e-32") assert.Equal(t, z1, resValue) } + +func TestUmaskOpt(t *testing.T) { + t.Run("unset by default", func(t *testing.T) { + var opt UmaskOpt + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "(unset)") + }) + + t.Run("rejects empty string", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("") + assert.Assert(t, err != nil) + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "(unset)") + }) + + t.Run("parses zero", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("0") + assert.NilError(t, err) + assert.Equal(t, *opt.Value(), uint32(0)) + assert.Equal(t, opt.String(), "0000") + }) + + t.Run("parses valid octal values", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("022") + assert.NilError(t, err) + assert.Assert(t, opt.Value() != nil) + assert.Equal(t, *opt.Value(), uint32(0o22)) + assert.Equal(t, opt.String(), "0022") + }) + + t.Run("rejects octal values with 0o prefix", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("0o22") + assert.Assert(t, err != nil) + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "(unset)") + }) + + t.Run("parses large octal values", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("077777") + assert.NilError(t, err) + assert.Assert(t, opt.Value() != nil) + assert.Equal(t, *opt.Value(), uint32(0o77777)) + assert.Equal(t, opt.String(), "077777") + }) + + t.Run("rejects invalid octal values", func(t *testing.T) { + var opt UmaskOpt + err := opt.Set("9") + assert.Assert(t, err != nil) + assert.Assert(t, opt.Value() == nil) + assert.Equal(t, opt.String(), "(unset)") + }) +}