Compare commits

..

No commits in common. "main" and "v1.2.10" have entirely different histories.

446 changed files with 7632 additions and 16464 deletions

View File

@ -11,8 +11,9 @@ env:
GOCACHE: "${HOME}/.cache/go-build"
# VM Image built in containers/automation_images
IMAGE_SUFFIX: "c20250324t111922z-f41f40d13"
IMAGE_SUFFIX: "c20230816t191118z-f38f37d13"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
RAWHIDE_CACHE_IMAGE_NAME: "rawhide-${IMAGE_SUFFIX}"
# Must be defined true when testing w/in containers
CONTAINER: "false"
@ -34,6 +35,7 @@ meta_task:
# Space-separated list of images used by this repository state
IMGNAMES: |-
${FEDORA_CACHE_IMAGE_NAME}
${RAWHIDE_CACHE_IMAGE_NAME}
BUILDID: "${CIRRUS_BUILD_ID}"
REPOREF: "${CIRRUS_CHANGE_IN_REPO}"
GCPJSON: ENCRYPTED[ef070c453a5ce68efca096a940835fdca530ed0ec2272ddb52bb02bf7d70dcc3ac9697b85b1d8dcce851931e008f63c0]
@ -48,7 +50,7 @@ meta_task:
vendor_task:
container:
image: golang:1.23
image: golang:1.19
script:
- make vendor
@ -69,6 +71,9 @@ build_and_test_task:
- name: "Test on Fedora"
gce_instance:
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
- name: "Test on Rawhide"
gce_instance:
image_name: "${RAWHIDE_CACHE_IMAGE_NAME}"
# Avoid downloading this stuff every time
gocache_cache:

View File

@ -6,7 +6,6 @@ GO_BUILD=$(GO) build
ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true)
GO_BUILD=GO111MODULE=on $(GO) build -mod=vendor
endif
BUILDDIR ?= .
DESTDIR ?=
PREFIX ?= /usr/local
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
@ -47,7 +46,7 @@ docs:
.PHONY: binary
binary:
$(GO_BUILD) -mod=vendor -o $(BUILDDIR)/bin/oci-seccomp-bpf-hook -ldflags "-X main.version=$(OSBH_VERSION)" $(PROJECT)
$(GO_BUILD) -mod=vendor -o bin/oci-seccomp-bpf-hook -ldflags "-X main.version=$(OSBH_VERSION)" $(PROJECT)
.PHONY: validate
validate:
@ -74,7 +73,7 @@ test-unit:
.PHONY: install.tools
install.tools: .install.golangci-lint .install.md2man
.install.golangci-lint: VERSION=v1.60.3
.install.golangci-lint: VERSION=v1.51.2
.install.golangci-lint:
curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/$(VERSION)/install.sh | sh -s -- -b ./build $(VERSION)
@ -95,7 +94,7 @@ install.docs:
install-nobuild: install.docs-nobuild
install $(SELINUXOPT) -d -m 755 $(DESTDIR)$(HOOK_BIN_DIR)
install $(SELINUXOPT) -d -m 755 $(DESTDIR)$(HOOK_DIR)
install $(SELINUXOPT) -m 755 $(BUILDDIR)/bin/oci-seccomp-bpf-hook $(DESTDIR)$(HOOK_BIN_DIR)
install $(SELINUXOPT) -m 755 bin/oci-seccomp-bpf-hook $(DESTDIR)$(HOOK_BIN_DIR)
install $(SELINUXOPT) -m 644 oci-seccomp-bpf-hook.json $(DESTDIR)$(HOOK_DIR)
sed -i 's|HOOK_BIN_DIR|$(HOOK_BIN_DIR)|g' $(DESTDIR)$(HOOK_DIR)/oci-seccomp-bpf-hook.json

View File

@ -1 +1 @@
v1.2.12-dev
v1.2.10

View File

@ -111,13 +111,6 @@ int enter_trace(struct tracepoint__raw_syscalls__sys_enter* args)
// The syscall was already notified.
if (seen > 0)
return 0;
// if prctl was not seen, ignore the current syscall.
u64 prctl = __NR_prctl;
u64 *prctl_seen = seen_syscalls.lookup(&prctl);
if (prctl_seen == NULL) {
return 0;
}
}
data.stopTracing = false;

24
go.mod
View File

@ -1,28 +1,26 @@
module github.com/containers/oci-seccomp-bpf-hook
go 1.23.0
toolchain go1.23.8
go 1.19
require (
github.com/containers/common v0.62.3
github.com/containers/storage v1.58.0
github.com/containers/common v0.56.0
github.com/containers/storage v1.50.2
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4
github.com/opencontainers/runtime-spec v1.2.1
github.com/opencontainers/runtime-spec v1.1.0
github.com/seccomp/libseccomp-golang v0.10.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.8.4
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
golang.org/x/sys v0.32.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/opencontainers/runc v1.1.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
golang.org/x/sys v0.12.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

40
go.sum
View File

@ -1,12 +1,11 @@
github.com/containers/common v0.62.3 h1:aOGryqXfW6aKBbHbqOveH7zB+ihavUN03X/2pUSvWFI=
github.com/containers/common v0.62.3/go.mod h1:3R8kDox2prC9uj/a2hmXj/YjZz5sBEUNrcDiw51S0Lo=
github.com/containers/storage v1.58.0 h1:Q7SyyCCjqgT3wYNgRNIL8o/wUS92heIj2/cc8Sewvcc=
github.com/containers/storage v1.58.0/go.mod h1:w7Jl6oG+OpeLGLzlLyOZPkmUso40kjpzgrHUk5tyBlo=
github.com/containers/common v0.56.0 h1:hysHUsEai1EkMXanU26UV55wMXns/a6AYmaFqJ4fEMY=
github.com/containers/common v0.56.0/go.mod h1:IjaDdfUtcs2CfCcJMZxuut4XlvkTkY9Nlqkso9xCOq4=
github.com/containers/storage v1.50.2 h1:Fys4BjFUVNRBEXlO70hFI48VW4EXsgnGisTpk9tTMsE=
github.com/containers/storage v1.50.2/go.mod h1:dpspZsUrcKD8SpTofvKWhwPDHD0MkO4Q7VE+oYdWkiA=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4 h1:WpizD4VUT5V+VcaQSvW5BlvFpQYrd2974H9KbiGa5/0=
@ -18,20 +17,16 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20241108202711-f7e3563b0271 h1:TPj0pMLCTy1CKwmrat3hqTxoZfqOuTy0asG0ccpGk8Q=
github.com/opencontainers/runtime-tools v0.9.1-0.20241108202711-f7e3563b0271/go.mod h1:oIH6VwKkaDOO+SIYZpdwrC/0wKYqrfO6E1sG1j3UVws=
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/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM=
github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20230317050512-e931285f4b69 h1:NL4xDvl68WWqQ+8WPMM3l5PsZTxaT7Z4K3VSKDRuAGs=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY=
@ -40,13 +35,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@ -228,13 +228,6 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
m := bcc.NewModule(src, []string{})
defer m.Close()
table := bcc.NewTable(m.TableId("events"), m)
channel := make(chan []byte)
perfMap, err := bcc.InitPerfMap(table, channel, nil)
if err != nil {
return fmt.Errorf("error initializing perf map: %v", err)
}
logrus.Info("Loading enter tracepoint")
enterTrace, err := m.LoadTracepoint("enter_trace")
if err != nil {
@ -254,6 +247,13 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
return fmt.Errorf("error attaching to tracepoint: %v", err)
}
table := bcc.NewTable(m.TableId("events"), m)
channel := make(chan []byte)
perfMap, err := bcc.InitPerfMap(table, channel, nil)
if err != nil {
return fmt.Errorf("error initializing perf map: %v", err)
}
// Initialize the wait group used to wait for the tracing to be finished.
wg.Add(1)
var events []event

View File

@ -51,16 +51,9 @@ func DefaultProfile() *Seccomp {
{
Names: []string{
"bdflush",
"cachestat",
"futex_requeue",
"futex_wait",
"futex_waitv",
"futex_wake",
"io_pgetevents",
"io_pgetevents_time64",
"kexec_file_load",
"kexec_load",
"map_shadow_stack",
"migrate_pages",
"move_pages",
"nfsservctl",
@ -75,9 +68,9 @@ func DefaultProfile() *Seccomp {
"pciconfig_write",
"sgetmask",
"ssetmask",
"swapcontext",
"swapoff",
"swapon",
"syscall",
"sysfs",
"uselib",
"userfaultfd",
@ -149,7 +142,6 @@ func DefaultProfile() *Seccomp {
"fchdir",
"fchmod",
"fchmodat",
"fchmodat2",
"fchown",
"fchown32",
"fchownat",
@ -317,6 +309,7 @@ func DefaultProfile() *Seccomp {
"pwritev2",
"read",
"readahead",
"readdir",
"readlink",
"readlinkat",
"readv",
@ -404,12 +397,15 @@ func DefaultProfile() *Seccomp {
"shmdt",
"shmget",
"shutdown",
"sigaction",
"sigaltstack",
"signal",
"signalfd",
"signalfd4",
"sigpending",
"sigprocmask",
"sigreturn",
"sigsuspend",
"socketcall",
"socketpair",
"splice",
@ -423,6 +419,7 @@ func DefaultProfile() *Seccomp {
"sync",
"sync_file_range",
"syncfs",
"syscall",
"sysinfo",
"syslog",
"tee",
@ -435,6 +432,7 @@ func DefaultProfile() *Seccomp {
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
@ -524,7 +522,6 @@ func DefaultProfile() *Seccomp {
{
Names: []string{
"sync_file_range2",
"swapcontext",
},
Action: ActAllow,
Args: []*Arg{},
@ -579,16 +576,6 @@ func DefaultProfile() *Seccomp {
Arches: []string{"s390", "s390x"},
},
},
{
Names: []string{
"riscv_flush_icache",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Arches: []string{"riscv64"},
},
},
{
Names: []string{
"open_by_handle_at",
@ -616,8 +603,8 @@ func DefaultProfile() *Seccomp {
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
@ -630,11 +617,11 @@ func DefaultProfile() *Seccomp {
},
{
Names: []string{
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
@ -897,50 +884,6 @@ func DefaultProfile() *Seccomp {
Caps: []string{"CAP_AUDIT_WRITE"},
},
},
{
Names: []string{
"bpf",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN", "CAP_BPF"},
},
},
{
Names: []string{
"bpf",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_BPF"},
},
},
{
Names: []string{
"perf_event_open",
},
Action: ActErrno,
Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
Caps: []string{"CAP_SYS_ADMIN", "CAP_BPF"},
},
},
{
Names: []string{
"perf_event_open",
},
Action: ActAllow,
Args: []*Arg{},
Includes: Filter{
Caps: []string{"CAP_PERFMON"},
},
},
}
return &Seccomp{

View File

@ -1,4 +1,5 @@
//go:build linux && seccomp
// +build linux,seccomp
package seccomp

View File

@ -1,4 +1,5 @@
//go:build seccomp
// +build seccomp
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases

View File

@ -55,16 +55,9 @@
{
"names": [
"bdflush",
"cachestat",
"futex_requeue",
"futex_wait",
"futex_waitv",
"futex_wake",
"io_pgetevents",
"io_pgetevents_time64",
"kexec_file_load",
"kexec_load",
"map_shadow_stack",
"migrate_pages",
"move_pages",
"nfsservctl",
@ -79,9 +72,9 @@
"pciconfig_write",
"sgetmask",
"ssetmask",
"swapcontext",
"swapoff",
"swapon",
"syscall",
"sysfs",
"uselib",
"userfaultfd",
@ -156,7 +149,6 @@
"fchdir",
"fchmod",
"fchmodat",
"fchmodat2",
"fchown",
"fchown32",
"fchownat",
@ -324,6 +316,7 @@
"pwritev2",
"read",
"readahead",
"readdir",
"readlink",
"readlinkat",
"readv",
@ -411,12 +404,15 @@
"shmdt",
"shmget",
"shutdown",
"sigaction",
"sigaltstack",
"signal",
"signalfd",
"signalfd4",
"sigpending",
"sigprocmask",
"sigreturn",
"sigsuspend",
"socketcall",
"socketpair",
"splice",
@ -430,6 +426,7 @@
"sync",
"sync_file_range",
"syncfs",
"syscall",
"sysinfo",
"syslog",
"tee",
@ -442,6 +439,7 @@
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
@ -563,8 +561,7 @@
},
{
"names": [
"sync_file_range2",
"swapcontext"
"sync_file_range2"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@ -644,20 +641,6 @@
},
"excludes": {}
},
{
"names": [
"riscv_flush_icache"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"arches": [
"riscv64"
]
},
"excludes": {}
},
{
"names": [
"open_by_handle_at"
@ -693,8 +676,8 @@
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns"
@ -711,11 +694,11 @@
},
{
"names": [
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns"
@ -1063,68 +1046,6 @@
]
},
"excludes": {}
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN",
"CAP_BPF"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_BPF"
]
},
"excludes": {}
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN",
"CAP_BPF"
]
},
"errnoRet": 1,
"errno": "EPERM"
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_PERFMON"
]
},
"excludes": {}
}
]
}

View File

@ -1,4 +1,5 @@
//go:build seccomp
// +build seccomp
// SPDX-License-Identifier: Apache-2.0

View File

@ -1,4 +1,5 @@
//go:build !linux || !seccomp
// +build !linux !seccomp
// SPDX-License-Identifier: Apache-2.0
@ -14,12 +15,12 @@ import (
var errNotSupported = errors.New("seccomp not enabled in this build")
// LoadProfile returns an error on unsupported systems
// LoadProfile returns an error on unsuppored systems
func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
return nil, errNotSupported
}
// GetDefaultProfile returns an error on unsupported systems
// GetDefaultProfile returns an error on unsuppored systems
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
return nil, errNotSupported
}

View File

@ -1,4 +1,5 @@
//go:build linux && seccomp
// +build linux,seccomp
package seccomp

View File

@ -1,4 +1,5 @@
//go:build seccomp
// +build seccomp
package seccomp

View File

@ -1,38 +0,0 @@
package fileutils
import (
"errors"
"os"
"syscall"
"golang.org/x/sys/unix"
)
// Exists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink is followed.
func Exists(path string) error {
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
// simply checking the existence of a file.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, 0)
if err != nil {
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}
// Lexists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink itself is checked.
func Lexists(path string) error {
// FreeBSD before 15.0 does not support the AT_SYMLINK_NOFOLLOW flag for
// faccessat. In this case, the call to faccessat will return EINVAL and
// we fall back to using Lstat.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
if errors.Is(err, syscall.EINVAL) {
_, err = os.Lstat(path)
return err
}
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}

View File

@ -1,33 +0,0 @@
//go:build !windows && !freebsd
package fileutils
import (
"os"
"golang.org/x/sys/unix"
)
// Exists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink is followed.
func Exists(path string) error {
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
// simply checking the existence of a file.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_EACCESS)
if err != nil {
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}
// Lexists checks whether a file or directory exists at the given path.
// If the path is a symlink, the symlink itself is checked.
func Lexists(path string) error {
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
// simply checking the existence of a file.
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW|unix.AT_EACCESS)
if err != nil {
return &os.PathError{Op: "faccessat", Path: path, Err: err}
}
return nil
}

View File

@ -1,18 +0,0 @@
package fileutils
import (
"os"
)
// Exists checks whether a file or directory exists at the given path.
func Exists(path string) error {
_, err := os.Stat(path)
return err
}
// Lexists checks whether a file or directory exists at the given path, without
// resolving symlinks
func Lexists(path string) error {
_, err := os.Lstat(path)
return err
}

View File

@ -1,371 +0,0 @@
package fileutils
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"text/scanner"
"github.com/sirupsen/logrus"
)
// PatternMatcher allows checking paths against a list of patterns
type PatternMatcher struct {
patterns []*Pattern
exclusions bool
}
// NewPatternMatcher creates a new matcher object for specific patterns that can
// be used later to match against patterns against paths
func NewPatternMatcher(patterns []string) (*PatternMatcher, error) {
pm := &PatternMatcher{
patterns: make([]*Pattern, 0, len(patterns)),
}
for _, p := range patterns {
// Eliminate leading and trailing whitespace.
p = strings.TrimSpace(p)
if p == "" {
continue
}
p = filepath.Clean(p)
newp := &Pattern{}
if p[0] == '!' {
if len(p) == 1 {
return nil, errors.New("illegal exclusion pattern: \"!\"")
}
newp.exclusion = true
p = strings.TrimPrefix(filepath.Clean(p[1:]), "/")
pm.exclusions = true
}
// Do some syntax checking on the pattern.
// filepath's Match() has some really weird rules that are inconsistent
// so instead of trying to dup their logic, just call Match() for its
// error state and if there is an error in the pattern return it.
// If this becomes an issue we can remove this since its really only
// needed in the error (syntax) case - which isn't really critical.
if _, err := filepath.Match(p, "."); err != nil {
return nil, err
}
newp.cleanedPattern = p
newp.dirs = strings.Split(p, string(os.PathSeparator))
pm.patterns = append(pm.patterns, newp)
}
return pm, nil
}
// Deprecated: Please use the `MatchesResult` method instead.
// Matches matches path against all the patterns. Matches is not safe to be
// called concurrently
func (pm *PatternMatcher) Matches(file string) (bool, error) {
matched := false
file = filepath.FromSlash(file)
for _, pattern := range pm.patterns {
negative := false
if pattern.exclusion {
negative = true
}
match, err := pattern.match(file)
if err != nil {
return false, err
}
if match {
matched = !negative
}
}
if matched {
logrus.Debugf("Skipping excluded path: %s", file)
}
return matched, nil
}
type MatchResult struct {
isMatched bool
matches, excludes uint
}
// Excludes returns true if the overall result is matched
func (m *MatchResult) IsMatched() bool {
return m.isMatched
}
// Excludes returns the amount of matches of an MatchResult
func (m *MatchResult) Matches() uint {
return m.matches
}
// Excludes returns the amount of excludes of an MatchResult
func (m *MatchResult) Excludes() uint {
return m.excludes
}
// MatchesResult verifies the provided filepath against all patterns.
// It returns the `*MatchResult` result for the patterns on success, otherwise
// an error. This method is not safe to be called concurrently.
func (pm *PatternMatcher) MatchesResult(file string) (res *MatchResult, err error) {
file = filepath.FromSlash(file)
res = &MatchResult{false, 0, 0}
for _, pattern := range pm.patterns {
negative := false
if pattern.exclusion {
negative = true
}
match, err := pattern.match(file)
if err != nil {
return nil, err
}
if match {
res.isMatched = !negative
if negative {
res.excludes++
} else {
res.matches++
}
}
}
if res.matches > 0 {
logrus.Debugf("Skipping excluded path: %s", file)
}
return res, nil
}
// IsMatch verifies the provided filepath against all patterns and returns true
// if it matches. A match is valid if the last match is a positive one.
// It returns an error on failure and is not safe to be called concurrently.
func (pm *PatternMatcher) IsMatch(file string) (matched bool, err error) {
res, err := pm.MatchesResult(file)
if err != nil {
return false, err
}
return res.isMatched, nil
}
// Exclusions returns true if any of the patterns define exclusions
func (pm *PatternMatcher) Exclusions() bool {
return pm.exclusions
}
// Patterns returns array of active patterns
func (pm *PatternMatcher) Patterns() []*Pattern {
return pm.patterns
}
// Pattern defines a single regexp used to filter file paths.
type Pattern struct {
cleanedPattern string
dirs []string
regexp *regexp.Regexp
exclusion bool
}
func (p *Pattern) String() string {
return p.cleanedPattern
}
// Exclusion returns true if this pattern defines exclusion
func (p *Pattern) Exclusion() bool {
return p.exclusion
}
func (p *Pattern) match(path string) (bool, error) {
if p.regexp == nil {
if err := p.compile(); err != nil {
return false, filepath.ErrBadPattern
}
}
b := p.regexp.MatchString(path)
return b, nil
}
func (p *Pattern) compile() error {
regStr := "^"
pattern := p.cleanedPattern
// Go through the pattern and convert it to a regexp.
// We use a scanner so we can support utf-8 chars.
var scan scanner.Scanner
scan.Init(strings.NewReader(pattern))
sl := string(os.PathSeparator)
escSL := sl
const bs = `\`
if sl == bs {
escSL += bs
}
for scan.Peek() != scanner.EOF {
ch := scan.Next()
if ch == '*' {
if scan.Peek() == '*' {
// is some flavor of "**"
scan.Next()
// Treat **/ as ** so eat the "/"
if string(scan.Peek()) == sl {
scan.Next()
}
if scan.Peek() == scanner.EOF {
// is "**EOF" - to align with .gitignore just accept all
regStr += ".*"
} else {
// is "**"
// Note that this allows for any # of /'s (even 0) because
// the .* will eat everything, even /'s
regStr += "(.*" + escSL + ")?"
}
} else {
// is "*" so map it to anything but "/"
regStr += "[^" + escSL + "]*"
}
} else if ch == '?' {
// "?" is any char except "/"
regStr += "[^" + escSL + "]"
} else if ch == '.' || ch == '$' {
// Escape some regexp special chars that have no meaning
// in golang's filepath.Match
regStr += bs + string(ch)
} else if ch == '\\' {
// escape next char.
if sl == bs {
// On windows map "\" to "\\", meaning an escaped backslash,
// and then just continue because filepath.Match on
// Windows doesn't allow escaping at all
regStr += escSL
continue
}
if scan.Peek() != scanner.EOF {
regStr += bs + string(scan.Next())
} else {
return filepath.ErrBadPattern
}
} else {
regStr += string(ch)
}
}
regStr += "(" + escSL + ".*)?$"
re, err := regexp.Compile(regStr)
if err != nil {
return err
}
p.regexp = re
return nil
}
// Matches returns true if file matches any of the patterns
// and isn't excluded by any of the subsequent patterns.
func Matches(file string, patterns []string) (bool, error) {
pm, err := NewPatternMatcher(patterns)
if err != nil {
return false, err
}
file = filepath.Clean(file)
if file == "." {
// Don't let them exclude everything, kind of silly.
return false, nil
}
return pm.IsMatch(file)
}
// CopyFile copies from src to dst until either EOF is reached
// on src or an error occurs. It verifies src exists and removes
// the dst if it exists.
func CopyFile(src, dst string) (int64, error) {
cleanSrc := filepath.Clean(src)
cleanDst := filepath.Clean(dst)
if cleanSrc == cleanDst {
return 0, nil
}
sf, err := os.Open(cleanSrc)
if err != nil {
return 0, err
}
defer sf.Close()
if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) {
return 0, err
}
df, err := os.Create(cleanDst)
if err != nil {
return 0, err
}
defer df.Close()
return io.Copy(df, sf)
}
// ReadSymlinkedDirectory returns the target directory of a symlink.
// The target of the symbolic link may not be a file.
func ReadSymlinkedDirectory(path string) (string, error) {
var realPath string
var err error
if realPath, err = filepath.Abs(path); err != nil {
return "", fmt.Errorf("unable to get absolute path for %s: %w", path, err)
}
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
return "", fmt.Errorf("failed to canonicalise path for %s: %w", path, err)
}
realPathInfo, err := os.Stat(realPath)
if err != nil {
return "", fmt.Errorf("failed to stat target '%s' of '%s': %w", realPath, path, err)
}
if !realPathInfo.Mode().IsDir() {
return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
}
return realPath, nil
}
// ReadSymlinkedPath returns the target directory of a symlink.
// The target of the symbolic link can be a file and a directory.
func ReadSymlinkedPath(path string) (realPath string, err error) {
if realPath, err = filepath.Abs(path); err != nil {
return "", fmt.Errorf("unable to get absolute path for %q: %w", path, err)
}
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
return "", fmt.Errorf("failed to canonicalise path for %q: %w", path, err)
}
if err := Exists(realPath); err != nil {
return "", fmt.Errorf("failed to stat target %q of %q: %w", realPath, path, err)
}
return realPath, nil
}
// CreateIfNotExists creates a file or a directory only if it does not already exist.
func CreateIfNotExists(path string, isDir bool) error {
if err := Exists(path); err != nil {
if os.IsNotExist(err) {
if isDir {
return os.MkdirAll(path, 0o755)
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
f, err := os.OpenFile(path, os.O_CREATE, 0o755)
if err != nil {
return err
}
f.Close()
}
}
return nil
}

View File

@ -1,27 +0,0 @@
package fileutils
import (
"os"
"os/exec"
"strconv"
"strings"
)
// GetTotalUsedFds returns the number of used File Descriptors by
// executing `lsof -p PID`
func GetTotalUsedFds() int {
pid := os.Getpid()
cmd := exec.Command("lsof", "-p", strconv.Itoa(pid))
output, err := cmd.CombinedOutput()
if err != nil {
return -1
}
outputStr := strings.TrimSpace(string(output))
fds := strings.Split(outputStr, "\n")
return len(fds) - 1
}

View File

@ -1,7 +0,0 @@
package fileutils
// GetTotalUsedFds Returns the number of used File Descriptors.
// On Solaris these limits are per process and not systemwide
func GetTotalUsedFds() int {
return -1
}

View File

@ -1,21 +0,0 @@
//go:build linux || freebsd
package fileutils
import (
"fmt"
"os"
"github.com/sirupsen/logrus"
)
// GetTotalUsedFds Returns the number of used File Descriptors by
// reading it via /proc filesystem.
func GetTotalUsedFds() int {
if fds, err := os.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
logrus.Errorf("%v", err)
} else {
return len(fds)
}
return -1
}

View File

@ -1,7 +0,0 @@
package fileutils
// GetTotalUsedFds Returns the number of used File Descriptors. Not supported
// on Windows.
func GetTotalUsedFds() int {
return -1
}

View File

@ -1,20 +0,0 @@
package fileutils
import (
"io"
"os"
"golang.org/x/sys/unix"
)
// ReflinkOrCopy attempts to reflink the source to the destination fd.
// If reflinking fails or is unsupported, it falls back to io.Copy().
func ReflinkOrCopy(src, dst *os.File) error {
err := unix.IoctlFileClone(int(dst.Fd()), int(src.Fd()))
if err == nil {
return nil
}
_, err = io.Copy(dst, src)
return err
}

View File

@ -1,15 +0,0 @@
//go:build !linux
package fileutils
import (
"io"
"os"
)
// ReflinkOrCopy attempts to reflink the source to the destination fd.
// If reflinking fails or is unsupported, it falls back to io.Copy().
func ReflinkOrCopy(src, dst *os.File) error {
_, err := io.Copy(dst, src)
return err
}

View File

@ -4,7 +4,6 @@ import (
"bufio"
"errors"
"fmt"
"io/fs"
"os"
"os/user"
"runtime"
@ -229,7 +228,7 @@ func getOverflowUID() int {
return overflowUID
}
// getOverflowGID returns the GID mapped to the overflow user
// getOverflowUID returns the GID mapped to the overflow user
func getOverflowGID() int {
overflowGIDOnce.Do(func() {
// 65534 is the value on older kernels where /proc/sys/kernel/overflowgid is not present
@ -368,174 +367,21 @@ func checkChownErr(err error, name string, uid, gid int) error {
return err
}
// Stat contains file states that can be overridden with ContainersOverrideXattr.
type Stat struct {
IDs IDPair
Mode os.FileMode
Major int
Minor int
}
// FormatContainersOverrideXattr will format the given uid, gid, and mode into a string
// that can be used as the value for the ContainersOverrideXattr xattr.
func FormatContainersOverrideXattr(uid, gid, mode int) string {
return FormatContainersOverrideXattrDevice(uid, gid, fs.FileMode(mode), 0, 0)
}
// FormatContainersOverrideXattrDevice will format the given uid, gid, and mode into a string
// that can be used as the value for the ContainersOverrideXattr xattr. For devices, it also
// needs the major and minor numbers.
func FormatContainersOverrideXattrDevice(uid, gid int, mode fs.FileMode, major, minor int) string {
typ := ""
switch mode & os.ModeType {
case os.ModeDir:
typ = "dir"
case os.ModeSymlink:
typ = "symlink"
case os.ModeNamedPipe:
typ = "pipe"
case os.ModeSocket:
typ = "socket"
case os.ModeDevice:
typ = fmt.Sprintf("block-%d-%d", major, minor)
case os.ModeDevice | os.ModeCharDevice:
typ = fmt.Sprintf("char-%d-%d", major, minor)
default:
typ = "file"
}
unixMode := mode & os.ModePerm
if mode&os.ModeSetuid != 0 {
unixMode |= 0o4000
}
if mode&os.ModeSetgid != 0 {
unixMode |= 0o2000
}
if mode&os.ModeSticky != 0 {
unixMode |= 0o1000
}
return fmt.Sprintf("%d:%d:%04o:%s", uid, gid, unixMode, typ)
}
// GetContainersOverrideXattr will get and decode ContainersOverrideXattr.
func GetContainersOverrideXattr(path string) (Stat, error) {
xstat, err := system.Lgetxattr(path, ContainersOverrideXattr)
if err != nil {
return Stat{}, err
}
return parseOverrideXattr(xstat) // This will fail if (xstat, err) == (nil, nil), i.e. the xattr does not exist.
}
func parseOverrideXattr(xstat []byte) (Stat, error) {
var stat Stat
attrs := strings.Split(string(xstat), ":")
if len(attrs) < 3 {
return stat, fmt.Errorf("the number of parts in %s is less than 3",
ContainersOverrideXattr)
}
value, err := strconv.ParseUint(attrs[0], 10, 32)
if err != nil {
return stat, fmt.Errorf("failed to parse UID: %w", err)
}
stat.IDs.UID = int(value)
value, err = strconv.ParseUint(attrs[1], 10, 32)
if err != nil {
return stat, fmt.Errorf("failed to parse GID: %w", err)
}
stat.IDs.GID = int(value)
value, err = strconv.ParseUint(attrs[2], 8, 32)
if err != nil {
return stat, fmt.Errorf("failed to parse mode: %w", err)
}
stat.Mode = os.FileMode(value) & os.ModePerm
if value&0o1000 != 0 {
stat.Mode |= os.ModeSticky
}
if value&0o2000 != 0 {
stat.Mode |= os.ModeSetgid
}
if value&0o4000 != 0 {
stat.Mode |= os.ModeSetuid
}
if len(attrs) > 3 {
typ := attrs[3]
if strings.HasPrefix(typ, "file") {
} else if strings.HasPrefix(typ, "dir") {
stat.Mode |= os.ModeDir
} else if strings.HasPrefix(typ, "symlink") {
stat.Mode |= os.ModeSymlink
} else if strings.HasPrefix(typ, "pipe") {
stat.Mode |= os.ModeNamedPipe
} else if strings.HasPrefix(typ, "socket") {
stat.Mode |= os.ModeSocket
} else if strings.HasPrefix(typ, "block") {
stat.Mode |= os.ModeDevice
stat.Major, stat.Minor, err = parseDevice(typ)
if err != nil {
return stat, err
}
} else if strings.HasPrefix(typ, "char") {
stat.Mode |= os.ModeDevice | os.ModeCharDevice
stat.Major, stat.Minor, err = parseDevice(typ)
if err != nil {
return stat, err
}
} else {
return stat, fmt.Errorf("invalid file type %s", typ)
}
}
return stat, nil
}
func parseDevice(typ string) (int, int, error) {
parts := strings.Split(typ, "-")
// If there are more than 3 parts, just ignore them to be forward compatible
if len(parts) < 3 {
return 0, 0, fmt.Errorf("invalid device type %s", typ)
}
if parts[0] != "block" && parts[0] != "char" {
return 0, 0, fmt.Errorf("invalid device type %s", typ)
}
major, err := strconv.Atoi(parts[1])
if err != nil {
return 0, 0, fmt.Errorf("failed to parse major number: %w", err)
}
minor, err := strconv.Atoi(parts[2])
if err != nil {
return 0, 0, fmt.Errorf("failed to parse minor number: %w", err)
}
return major, minor, nil
}
// SetContainersOverrideXattr will encode and set ContainersOverrideXattr.
func SetContainersOverrideXattr(path string, stat Stat) error {
value := FormatContainersOverrideXattrDevice(stat.IDs.UID, stat.IDs.GID, stat.Mode, stat.Major, stat.Minor)
return system.Lsetxattr(path, ContainersOverrideXattr, []byte(value), 0)
}
func SafeChown(name string, uid, gid int) error {
if runtime.GOOS == "darwin" {
stat := Stat{
Mode: os.FileMode(0o0700),
}
var mode uint64 = 0o0700
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
if err == nil && xstat != nil {
stat, err = parseOverrideXattr(xstat)
if err != nil {
return err
if err == nil {
attrs := strings.Split(string(xstat), ":")
if len(attrs) == 3 {
val, err := strconv.ParseUint(attrs[2], 8, 32)
if err == nil {
mode = val
}
} else {
st, err := os.Stat(name) // Ideally we would share this with system.Stat below, but then we would need to convert Mode.
if err != nil {
return err
}
stat.Mode = st.Mode()
}
stat.IDs = IDPair{UID: uid, GID: gid}
if err = SetContainersOverrideXattr(name, stat); err != nil {
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
if err = system.Lsetxattr(name, ContainersOverrideXattr, []byte(value), 0); err != nil {
return err
}
uid = os.Getuid()
@ -551,24 +397,19 @@ func SafeChown(name string, uid, gid int) error {
func SafeLchown(name string, uid, gid int) error {
if runtime.GOOS == "darwin" {
stat := Stat{
Mode: os.FileMode(0o0700),
}
var mode uint64 = 0o0700
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
if err == nil && xstat != nil {
stat, err = parseOverrideXattr(xstat)
if err != nil {
return err
if err == nil {
attrs := strings.Split(string(xstat), ":")
if len(attrs) == 3 {
val, err := strconv.ParseUint(attrs[2], 8, 32)
if err == nil {
mode = val
}
} else {
st, err := os.Lstat(name) // Ideally we would share this with system.Stat below, but then we would need to convert Mode.
if err != nil {
return err
}
stat.Mode = st.Mode()
}
stat.IDs = IDPair{UID: uid, GID: gid}
if err = SetContainersOverrideXattr(name, stat); err != nil {
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
if err = system.Lsetxattr(name, ContainersOverrideXattr, []byte(value), 0); err != nil {
return err
}
uid = os.Getuid()

View File

@ -1,11 +1,11 @@
//go:build linux && cgo && libsubid
// +build linux,cgo,libsubid
package idtools
import (
"errors"
"os/user"
"sync"
"unsafe"
)
@ -14,14 +14,16 @@ import (
#include <shadow/subid.h>
#include <stdlib.h>
#include <stdio.h>
const char *Prog = "storage";
FILE *shadow_logfd = NULL;
struct subid_range get_range(struct subid_range *ranges, int i)
{
shadow_logfd = stderr;
return ranges[i];
}
#if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4)
# define subid_init libsubid_init
# define subid_get_uid_ranges get_subuid_ranges
# define subid_get_gid_ranges get_subgid_ranges
#endif
@ -29,8 +31,6 @@ struct subid_range get_range(struct subid_range *ranges, int i)
*/
import "C"
var onceInit sync.Once
func readSubid(username string, isUser bool) (ranges, error) {
var ret ranges
uidstr := ""
@ -43,10 +43,6 @@ func readSubid(username string, isUser bool) (ranges, error) {
uidstr = u.Uid
}
onceInit.Do(func() {
C.subid_init(C.CString("storage"), C.stderr)
})
cUsername := C.CString(username)
defer C.free(unsafe.Pointer(cUsername))

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package idtools
@ -12,9 +13,8 @@ import (
"sync"
"syscall"
"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/system"
"github.com/moby/sys/user"
"github.com/opencontainers/runc/libcontainer/user"
)
var (
@ -55,7 +55,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
if dirPath == "/" {
break
}
if err := fileutils.Exists(dirPath); err != nil && os.IsNotExist(err) {
if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) {
paths = append(paths, dirPath)
}
}

View File

@ -1,4 +1,5 @@
//go:build !linux || !libsubid || !cgo
// +build !linux !libsubid !cgo
package idtools

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package idtools

View File

@ -1,4 +1,5 @@
//go:build !linux
// +build !linux
package idtools

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package idtools

View File

@ -97,14 +97,14 @@ func MergeTmpfsOptions(options []string) ([]string, error) {
}
continue
}
opt, _, ok := strings.Cut(option, "=")
if !ok || !validFlags[opt] {
opt := strings.SplitN(option, "=", 2)
if len(opt) != 2 || !validFlags[opt[0]] {
return nil, fmt.Errorf("invalid tmpfs option %q", opt)
}
if !dataCollisions[opt] {
if !dataCollisions[opt[0]] {
// We prepend the option and add to collision map
newOptions = append([]string{option}, newOptions...)
dataCollisions[opt] = true
dataCollisions[opt[0]] = true
}
}
@ -140,8 +140,8 @@ func ParseOptions(options string) (int, string) {
func ParseTmpfsOptions(options string) (int, string, error) {
flags, data := ParseOptions(options)
for _, o := range strings.Split(data, ",") {
opt, _, _ := strings.Cut(o, "=")
if !validFlags[opt] {
opt := strings.SplitN(o, "=", 2)
if !validFlags[opt[0]] {
return 0, "", fmt.Errorf("invalid tmpfs option %q", opt)
}
}

View File

@ -1,4 +1,5 @@
//go:build !linux && !freebsd
// +build !linux,!freebsd
package mount

View File

@ -1,4 +1,5 @@
//go:build freebsd && cgo
// +build freebsd,cgo
package mount
@ -39,9 +40,13 @@ func mount(device, target, mType string, flag uintptr, data string) error {
isNullFS = true
continue
}
name, val, _ := strings.Cut(x, "=")
options = append(options, name)
options = append(options, val)
opt := strings.SplitN(x, "=", 2)
options = append(options, opt[0])
if len(opt) == 2 {
options = append(options, opt[1])
} else {
options = append(options, "")
}
}
}

View File

@ -1,4 +1,6 @@
//go:build !linux && !(freebsd && cgo)
// +build !linux
// +build !freebsd !cgo
package mount

View File

@ -1,18 +1,5 @@
package mount
import (
"fmt"
"os"
import "github.com/moby/sys/mountinfo"
"github.com/moby/sys/mountinfo"
)
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 mountinfo.GetMountsFromReader(f, nil)
}
var PidMountInfo = mountinfo.PidMountInfo

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package mount
@ -10,7 +11,7 @@ import (
func unmount(target string, flags int) error {
var err error
for range 50 {
for i := 0; i < 50; i++ {
err = unix.Unmount(target, flags)
switch err {
case unix.EBUSY:

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package mount

View File

@ -1,4 +1,5 @@
//go:build freebsd
// +build freebsd
package reexec

View File

@ -1,4 +1,5 @@
//go:build linux
// +build linux
package reexec

View File

@ -1,4 +1,5 @@
//go:build solaris || darwin
// +build solaris darwin
package reexec

View File

@ -1,4 +1,5 @@
//go:build !linux && !windows && !freebsd && !solaris && !darwin
// +build !linux,!windows,!freebsd,!solaris,!darwin
package reexec

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package reexec

View File

@ -49,7 +49,7 @@ func panicIfNotInitialized() {
}
}
func naiveSelf() string {
func naiveSelf() string { //nolint: unused
name := os.Args[0]
if filepath.Base(name) == name {
if lp, err := exec.LookPath(name); err == nil {

View File

@ -1,4 +1,5 @@
//go:build !regexp_precompile
// +build !regexp_precompile
package regexp

View File

@ -1,4 +1,5 @@
//go:build regexp_precompile
// +build regexp_precompile
package regexp

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package system

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package system

View File

@ -1,93 +0,0 @@
//go:build freebsd
package system
import (
"os"
"unsafe"
"golang.org/x/sys/unix"
)
const (
EXTATTR_NAMESPACE_EMPTY = unix.EXTATTR_NAMESPACE_EMPTY
EXTATTR_NAMESPACE_USER = unix.EXTATTR_NAMESPACE_USER
EXTATTR_NAMESPACE_SYSTEM = unix.EXTATTR_NAMESPACE_SYSTEM
)
// ExtattrGetLink retrieves the value of the extended attribute identified by attrname
// in the given namespace and associated with the given path in the file system.
// If the path is a symbolic link, the extended attribute is retrieved from the link itself.
// Returns a []byte slice if the extattr is set and nil otherwise.
func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) {
size, errno := unix.ExtattrGetLink(path, attrnamespace, attrname,
uintptr(unsafe.Pointer(nil)), 0)
if errno != nil {
if errno == unix.ENOATTR {
return nil, nil
}
return nil, &os.PathError{Op: "extattr_get_link", Path: path, Err: errno}
}
if size == 0 {
return []byte{}, nil
}
dest := make([]byte, size)
size, errno = unix.ExtattrGetLink(path, attrnamespace, attrname,
uintptr(unsafe.Pointer(&dest[0])), size)
if errno != nil {
return nil, &os.PathError{Op: "extattr_get_link", Path: path, Err: errno}
}
return dest[:size], nil
}
// ExtattrSetLink sets the value of extended attribute identified by attrname
// in the given namespace and associated with the given path in the file system.
// If the path is a symbolic link, the extended attribute is set on the link itself.
func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error {
if len(data) == 0 {
data = []byte{} // ensure non-nil for empty data
}
if _, errno := unix.ExtattrSetLink(path, attrnamespace, attrname,
uintptr(unsafe.Pointer(&data[0])), len(data)); errno != nil {
return &os.PathError{Op: "extattr_set_link", Path: path, Err: errno}
}
return nil
}
// ExtattrListLink lists extended attributes associated with the given path
// in the specified namespace. If the path is a symbolic link, the attributes
// are listed from the link itself.
func ExtattrListLink(path string, attrnamespace int) ([]string, error) {
size, errno := unix.ExtattrListLink(path, attrnamespace,
uintptr(unsafe.Pointer(nil)), 0)
if errno != nil {
return nil, &os.PathError{Op: "extattr_list_link", Path: path, Err: errno}
}
if size == 0 {
return []string{}, nil
}
dest := make([]byte, size)
size, errno = unix.ExtattrListLink(path, attrnamespace,
uintptr(unsafe.Pointer(&dest[0])), size)
if errno != nil {
return nil, &os.PathError{Op: "extattr_list_link", Path: path, Err: errno}
}
var attrs []string
for i := 0; i < size; {
// Each attribute is preceded by a single byte length
length := int(dest[i])
i++
if i+length > size {
break
}
attrs = append(attrs, string(dest[i:i+length]))
i += length
}
return attrs, nil
}

View File

@ -1,24 +0,0 @@
//go:build !freebsd
package system
const (
EXTATTR_NAMESPACE_EMPTY = 0
EXTATTR_NAMESPACE_USER = 0
EXTATTR_NAMESPACE_SYSTEM = 0
)
// ExtattrGetLink is not supported on platforms other than FreeBSD.
func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) {
return nil, ErrNotSupportedPlatform
}
// ExtattrSetLink is not supported on platforms other than FreeBSD.
func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error {
return ErrNotSupportedPlatform
}
// ExtattrListLink is not supported on platforms other than FreeBSD.
func ExtattrListLink(path string, attrnamespace int) ([]string, error) {
return nil, ErrNotSupportedPlatform
}

View File

@ -1,4 +1,5 @@
//go:build freebsd
// +build freebsd
package system

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package system

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package system

View File

@ -1,4 +1,5 @@
//go:build freebsd && cgo
// +build freebsd,cgo
package system

View File

@ -1,4 +1,5 @@
//go:build solaris && cgo
// +build solaris,cgo
package system

View File

@ -1,4 +1,8 @@
//go:build !linux && !windows && !solaris && !(freebsd && cgo)
// +build !linux
// +build !windows
// +build !solaris
// +build !freebsd !cgo
package system

View File

@ -1,4 +1,5 @@
//go:build !windows && !freebsd
// +build !windows,!freebsd
package system

View File

@ -1,4 +1,5 @@
//go:build freebsd
// +build freebsd
package system

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package system

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package system

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package system

View File

@ -1,4 +1,5 @@
//go:build linux || freebsd || solaris || darwin
// +build linux freebsd solaris darwin
package system

View File

@ -28,7 +28,7 @@ func EnsureRemoveAll(dir string) error {
// track retries
exitOnErr := make(map[string]int)
maxRetry := 1000
maxRetry := 100
// Attempt a simple remove all first, this avoids the more expensive
// RecursiveUnmount call if not needed.
@ -38,7 +38,7 @@ func EnsureRemoveAll(dir string) error {
// Attempt to unmount anything beneath this dir first
if err := mount.RecursiveUnmount(dir); err != nil {
logrus.Debugf("RecursiveUnmount on %s failed: %v", dir, err)
logrus.Debugf("RecusiveUnmount on %s failed: %v", dir, err)
}
for {
@ -94,6 +94,6 @@ func EnsureRemoveAll(dir string) error {
return err
}
exitOnErr[pe.Path]++
time.Sleep(10 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
}
}

View File

@ -1,4 +1,5 @@
//go:build !freebsd
// +build !freebsd
package system

View File

@ -1,4 +1,5 @@
//go:build !freebsd
// +build !freebsd
package system

View File

@ -9,9 +9,9 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) {
mode: s.Mode,
uid: s.Uid,
gid: s.Gid,
rdev: uint64(s.Rdev), //nolint:unconvert
rdev: uint64(s.Rdev),
mtim: s.Mtim,
dev: uint64(s.Dev), //nolint:unconvert
dev: uint64(s.Dev),
}, nil
}

View File

@ -1,13 +0,0 @@
package system
import "syscall"
// fromStatT converts a syscall.Stat_t type to a system.Stat_t type
func fromStatT(s *syscall.Stat_t) (*StatT, error) {
return &StatT{size: s.Size,
mode: uint32(s.Mode),
uid: s.Uid,
gid: s.Gid,
rdev: uint64(s.Rdev),
mtim: s.Mtimespec}, nil
}

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package system

View File

@ -1,4 +1,5 @@
//go:build !windows
//go:build linux || freebsd || darwin
// +build linux freebsd darwin
package system

View File

@ -1,4 +1,5 @@
//go:build !windows
// +build !windows
package system

View File

@ -1,4 +1,5 @@
//go:build windows
// +build windows
package system

View File

@ -1,4 +1,5 @@
//go:build !linux && !freebsd
// +build !linux,!freebsd
package system

View File

@ -12,7 +12,7 @@ const (
E2BIG unix.Errno = unix.E2BIG
// Operation not supported
ENOTSUP unix.Errno = unix.ENOTSUP
EOPNOTSUPP unix.Errno = unix.EOPNOTSUPP
)
// Lgetxattr retrieves the value of the extended attribute identified by attr

View File

@ -1,85 +0,0 @@
package system
import (
"strings"
"golang.org/x/sys/unix"
)
const (
// Value is larger than the maximum size allowed
E2BIG unix.Errno = unix.E2BIG
// Operation not supported
ENOTSUP unix.Errno = unix.ENOTSUP
// Value is too small or too large for maximum size allowed
EOVERFLOW unix.Errno = unix.EOVERFLOW
)
var (
namespaceMap = map[string]int{
"user": EXTATTR_NAMESPACE_USER,
"system": EXTATTR_NAMESPACE_SYSTEM,
}
)
func xattrToExtattr(xattr string) (namespace int, extattr string, err error) {
namespaceName, extattr, found := strings.Cut(xattr, ".")
if !found {
return -1, "", ENOTSUP
}
namespace, ok := namespaceMap[namespaceName]
if !ok {
return -1, "", ENOTSUP
}
return namespace, extattr, nil
}
// Lgetxattr retrieves the value of the extended attribute identified by attr
// and associated with the given path in the file system.
// Returns a []byte slice if the xattr is set and nil otherwise.
func Lgetxattr(path string, attr string) ([]byte, error) {
namespace, extattr, err := xattrToExtattr(attr)
if err != nil {
return nil, err
}
return ExtattrGetLink(path, namespace, extattr)
}
// Lsetxattr sets the value of the extended attribute identified by attr
// and associated with the given path in the file system.
func Lsetxattr(path string, attr string, value []byte, flags int) error {
if flags != 0 {
// FIXME: Flags are not supported on FreeBSD, but we can implement
// them mimicking the behavior of the Linux implementation.
// See lsetxattr(2) on Linux for more information.
return ENOTSUP
}
namespace, extattr, err := xattrToExtattr(attr)
if err != nil {
return err
}
return ExtattrSetLink(path, namespace, extattr, value)
}
// Llistxattr lists extended attributes associated with the given path
// in the file system.
func Llistxattr(path string) ([]string, error) {
attrs := []string{}
for namespaceName, namespace := range namespaceMap {
namespaceAttrs, err := ExtattrListLink(path, namespace)
if err != nil {
return nil, err
}
for _, attr := range namespaceAttrs {
attrs = append(attrs, namespaceName+"."+attr)
}
}
return attrs, nil
}

View File

@ -12,7 +12,7 @@ const (
E2BIG unix.Errno = unix.E2BIG
// Operation not supported
ENOTSUP unix.Errno = unix.ENOTSUP
EOPNOTSUPP unix.Errno = unix.EOPNOTSUPP
// Value is too small or too large for maximum size allowed
EOVERFLOW unix.Errno = unix.EOVERFLOW

View File

@ -1,4 +1,5 @@
//go:build !linux && !darwin && !freebsd
//go:build !linux && !darwin
// +build !linux,!darwin
package system
@ -9,7 +10,7 @@ const (
E2BIG syscall.Errno = syscall.Errno(0)
// Operation not supported
ENOTSUP syscall.Errno = syscall.Errno(0)
EOPNOTSUPP syscall.Errno = syscall.Errno(0)
// Value is too small or too large for maximum size allowed
EOVERFLOW syscall.Errno = syscall.Errno(0)

View File

@ -1,4 +1,5 @@
//go:build linux && cgo
// +build linux,cgo
package unshare

View File

@ -1,4 +1,5 @@
//go:build linux && !cgo
// +build linux,!cgo
package unshare

View File

@ -15,7 +15,6 @@
#include <termios.h>
#include <errno.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/vfs.h>
#include <sys/mount.h>
#include <linux/limits.h>

View File

@ -1,4 +1,5 @@
//go:build (linux && cgo && !gccgo) || (freebsd && cgo)
// +build linux,cgo,!gccgo freebsd,cgo
package unshare

View File

@ -1,4 +1,5 @@
//go:build darwin
// +build darwin
package unshare
@ -24,11 +25,6 @@ func GetRootlessUID() int {
return os.Getuid()
}
// GetRootlessGID returns the GID of the user in the parent userNS
func GetRootlessGID() int {
return os.Getgid()
}
// RootlessEnv returns the environment settings for the rootless containers
func RootlessEnv() []string {
return append(os.Environ(), UsernsEnvName+"=")

View File

@ -1,4 +1,5 @@
//go:build freebsd
// +build freebsd
package unshare

View File

@ -1,4 +1,5 @@
//go:build linux && cgo && gccgo
// +build linux,cgo,gccgo
package unshare

View File

@ -1,4 +1,5 @@
//go:build linux
// +build linux
package unshare
@ -20,9 +21,9 @@ import (
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/reexec"
"github.com/moby/sys/capability"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/syndtr/gocapability/capability"
)
// Cmd wraps an exec.Cmd created by the reexec package in unshare(), and
@ -32,9 +33,9 @@ type Cmd struct {
*exec.Cmd
UnshareFlags int
UseNewuidmap bool
UidMappings []specs.LinuxIDMapping //nolint: revive
UidMappings []specs.LinuxIDMapping // nolint: revive,golint
UseNewgidmap bool
GidMappings []specs.LinuxIDMapping //nolint: revive
GidMappings []specs.LinuxIDMapping // nolint: revive,golint
GidMappingsEnableSetgroups bool
Setsid bool
Setpgrp bool
@ -98,7 +99,7 @@ func IsSetID(path string, modeid os.FileMode, capid capability.Cap) (bool, error
return cap.Get(capability.EFFECTIVE, capid), nil
}
func (c *Cmd) Start() (retErr error) {
func (c *Cmd) Start() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@ -167,15 +168,6 @@ func (c *Cmd) Start() (retErr error) {
return err
}
// If the function fails from here, we need to make sure the
// child process is killed and properly cleaned up.
defer func() {
if retErr != nil {
_ = c.Cmd.Process.Kill()
_ = c.Cmd.Wait()
}
}()
// Close the ends of the pipes that the parent doesn't need.
continueRead.Close()
continueRead = nil
@ -249,7 +241,7 @@ func (c *Cmd) Start() (retErr error) {
if err != nil {
return fmt.Errorf("finding newgidmap: %w", err)
}
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(g.String())...)...)
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(strings.Replace(g.String(), "\n", " ", -1))...)...)
g.Reset()
cmd.Stdout = g
cmd.Stderr = g
@ -267,7 +259,7 @@ func (c *Cmd) Start() (retErr error) {
}
logrus.Warnf("Falling back to single mapping")
g.Reset()
fmt.Fprintf(g, "0 %d 1\n", os.Getegid())
g.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Getegid())))
}
}
if !gidmapSet {
@ -309,7 +301,7 @@ func (c *Cmd) Start() (retErr error) {
if err != nil {
return fmt.Errorf("finding newuidmap: %w", err)
}
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(u.String())...)...)
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(strings.Replace(u.String(), "\n", " ", -1))...)...)
u.Reset()
cmd.Stdout = u
cmd.Stderr = u
@ -328,7 +320,7 @@ func (c *Cmd) Start() (retErr error) {
logrus.Warnf("Falling back to single mapping")
u.Reset()
fmt.Fprintf(u, "0 %d 1\n", os.Geteuid())
u.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Geteuid())))
}
}
if !uidmapSet {
@ -449,16 +441,6 @@ func GetRootlessUID() int {
return os.Getuid()
}
// GetRootlessGID returns the GID of the user in the parent userNS
func GetRootlessGID() int {
gidEnv := getenv("_CONTAINERS_ROOTLESS_GID")
if gidEnv != "" {
u, _ := strconv.Atoi(gidEnv)
return u
}
return os.Getgid()
}
// RootlessEnv returns the environment settings for the rootless containers
func RootlessEnv() []string {
return append(os.Environ(), UsernsEnvName+"=done")
@ -468,7 +450,7 @@ type Runnable interface {
Run() error
}
func bailOnError(err error, format string, a ...any) { //nolint:revive,goprintffuncname
func bailOnError(err error, format string, a ...interface{}) { // nolint: revive,goprintffuncname
if err != nil {
if format != "" {
logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
@ -534,11 +516,8 @@ func MaybeReexecUsingUserNamespace(evenForRoot bool) {
} else {
// If we have CAP_SYS_ADMIN, then we don't need to create a new namespace in order to be able
// to use unshare(), so don't bother creating a new user namespace at this point.
capabilities, err := capability.NewPid2(0)
bailOnError(err, "Initializing a new Capabilities object of pid 0")
err = capabilities.Load()
capabilities, err := capability.NewPid(0)
bailOnError(err, "Reading the current capabilities sets")
if capabilities.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) {
return
}
@ -598,12 +577,7 @@ func MaybeReexecUsingUserNamespace(evenForRoot bool) {
cmd.Hook = func(int) error {
go func() {
for receivedSignal := range interrupted {
if err := cmd.Cmd.Process.Signal(receivedSignal); err != nil {
logrus.Warnf(
"Failed to send a signal '%d' to the Process (PID: %d): %v",
receivedSignal, cmd.Cmd.Process.Pid, err,
)
}
cmd.Cmd.Process.Signal(receivedSignal)
}
}()
return nil

View File

@ -1,4 +1,5 @@
//go:build !linux && !darwin
// +build !linux,!darwin
package unshare
@ -16,7 +17,7 @@ const (
// IsRootless tells us if we are running in rootless mode
func IsRootless() bool {
return os.Getuid() != 0
return false
}
// GetRootlessUID returns the UID of the user in the parent userNS
@ -24,11 +25,6 @@ func GetRootlessUID() int {
return os.Getuid()
}
// GetRootlessGID returns the GID of the user in the parent userNS
func GetRootlessGID() int {
return os.Getgid()
}
// RootlessEnv returns the environment settings for the rootless containers
func RootlessEnv() []string {
return append(os.Environ(), UsernsEnvName+"=")

View File

@ -1,4 +1,5 @@
//go:build cgo && !(linux || freebsd)
// +build cgo,!linux,!freebsd
package unshare

View File

@ -1,124 +0,0 @@
# Changelog
This file documents all notable changes made to this project since the initial fork
from https://github.com/syndtr/gocapability/commit/42c35b4376354fd5.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.4.0] - 2024-11-11
### Added
* New separate API for ambient ([GetAmbient], [SetAmbient], [ResetAmbient])
and bound ([GetBound], [DropBound]) capabilities, modelled after libcap. (#176)
### Fixed
* [Apply] now returns an error if called for non-zero `pid`. Before this change,
it could silently change some capabilities of the current process, instead of
the one identified by the `pid`. (#168, #174)
* Fixed tests that change capabilities to be run in a separate process. (#173)
* Other improvements in tests. (#169, #170)
### Changed
* Use raw syscalls (which are slightly faster). (#176)
* Most tests are now limited to testing the public API of the package. (#162)
* Simplify parsing /proc/*pid*/status, add a test case. (#162)
* Optimize the number of syscall to set ambient capabilities in Apply
by clearing them first; add a test case. (#163, #164)
* Better documentation for [Apply], [NewFile], [NewFile2], [NewPid], [NewPid2]. (#175)
### Removed
* `.golangci.yml` and `.codespellrc` are no longer part of the package. (#158)
## [0.3.0] - 2024-09-25
### Added
* Added [ListKnown] and [ListSupported] functions. (#153)
* [LastCap] is now available on non-Linux platforms (where it returns an error). (#152)
### Changed
* [List] is now deprecated in favor of [ListKnown] and [ListSupported]. (#153)
### Fixed
* Various documentation improvements. (#151)
* Fix "generated code" comment. (#153)
## [0.2.0] - 2024-09-16
This is the first release after the move to a new home in
github.com/moby/sys/capability.
### Fixed
* Fixed URLs in documentation to reflect the new home.
## [0.1.1] - 2024-08-01
This is a maintenance release, fixing a few minor issues.
### Fixed
* Fixed future kernel compatibility, for real this time. [#11]
* Fixed [LastCap] to be a function. [#12]
## [0.1.0] - 2024-07-31
This is an initial release since the fork.
### Breaking changes
* The `CAP_LAST_CAP` variable is removed; users need to modify the code to
use [LastCap] to get the value. [#6]
* The code now requires Go >= 1.21.
### Added
* `go.mod` and `go.sum` files. [#2]
* New [LastCap] function. [#6]
* Basic CI using GHA infra. [#8], [#9]
* README and CHANGELOG. [#10]
### Fixed
* Fixed ambient capabilities error handling in [Apply]. [#3]
* Fixed future kernel compatibility. [#1]
* Fixed various linter warnings. [#4], [#7]
### Changed
* Go build tags changed from old-style (`+build`) to new Go 1.17+ style (`go:build`). [#2]
### Removed
* Removed support for capabilities v1 and v2. [#1]
* Removed init function so programs that use this package start faster. [#6]
* Removed `CAP_LAST_CAP` (use [LastCap] instead). [#6]
<!-- Doc links (please keep sorted). -->
[Apply]: https://pkg.go.dev/github.com/moby/sys/capability#Capabilities.Apply
[DropBound]: https://pkg.go.dev/github.com/moby/sys/capability#DropBound
[GetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#GetAmbient
[GetBound]: https://pkg.go.dev/github.com/moby/sys/capability#GetBound
[LastCap]: https://pkg.go.dev/github.com/moby/sys/capability#LastCap
[ListKnown]: https://pkg.go.dev/github.com/moby/sys/capability#ListKnown
[ListSupported]: https://pkg.go.dev/github.com/moby/sys/capability#ListSupported
[List]: https://pkg.go.dev/github.com/moby/sys/capability#List
[NewFile2]: https://pkg.go.dev/github.com/moby/sys/capability#NewFile2
[NewFile]: https://pkg.go.dev/github.com/moby/sys/capability#NewFile
[NewPid2]: https://pkg.go.dev/github.com/moby/sys/capability#NewPid2
[NewPid]: https://pkg.go.dev/github.com/moby/sys/capability#NewPid
[ResetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#ResetAmbient
[SetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#SetAmbient
<!-- Minor releases. -->
[0.4.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.4.0
[0.3.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.3.0
[0.2.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.2.0
[0.1.1]: https://github.com/kolyshkin/capability/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/kolyshkin/capability/compare/42c35b4376354fd5...v0.1.0
<!-- PRs in 0.1.x releases. -->
[#1]: https://github.com/kolyshkin/capability/pull/1
[#2]: https://github.com/kolyshkin/capability/pull/2
[#3]: https://github.com/kolyshkin/capability/pull/3
[#4]: https://github.com/kolyshkin/capability/pull/4
[#6]: https://github.com/kolyshkin/capability/pull/6
[#7]: https://github.com/kolyshkin/capability/pull/7
[#8]: https://github.com/kolyshkin/capability/pull/8
[#9]: https://github.com/kolyshkin/capability/pull/9
[#10]: https://github.com/kolyshkin/capability/pull/10
[#11]: https://github.com/kolyshkin/capability/pull/11
[#12]: https://github.com/kolyshkin/capability/pull/12

View File

@ -1,13 +0,0 @@
This is a fork of (apparently no longer maintained)
https://github.com/syndtr/gocapability package. It provides basic primitives to
work with [Linux capabilities][capabilities(7)].
For changes, see [CHANGELOG.md](./CHANGELOG.md).
[![Go Reference](https://pkg.go.dev/badge/github.com/moby/sys/capability/capability.svg)](https://pkg.go.dev/github.com/moby/sys/capability)
## Alternatives
* https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/cap
[capabilities(7)]: https://man7.org/linux/man-pages/man7/capabilities.7.html

View File

@ -1,176 +0,0 @@
// Copyright 2023 The Capability Authors.
// Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package capability provides utilities for manipulating POSIX capabilities.
package capability
type Capabilities interface {
// Get check whether a capability present in the given
// capabilities set. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Get(which CapType, what Cap) bool
// Empty check whether all capability bits of the given capabilities
// set are zero. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Empty(which CapType) bool
// Full check whether all capability bits of the given capabilities
// set are one. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Full(which CapType) bool
// Set sets capabilities of the given capabilities sets. The
// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Set(which CapType, caps ...Cap)
// Unset unsets capabilities of the given capabilities sets. The
// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Unset(which CapType, caps ...Cap)
// Fill sets all bits of the given capabilities kind to one. The
// 'kind' value should be one or combination (OR'ed) of CAPS,
// BOUNDS or AMBS.
Fill(kind CapType)
// Clear sets all bits of the given capabilities kind to zero. The
// 'kind' value should be one or combination (OR'ed) of CAPS,
// BOUNDS or AMBS.
Clear(kind CapType)
// String return current capabilities state of the given capabilities
// set as string. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE BOUNDING or AMBIENT
StringCap(which CapType) string
// String return current capabilities state as string.
String() string
// Load load actual capabilities value. This will overwrite all
// outstanding changes.
Load() error
// Apply apply the capabilities settings, so all changes made by
// [Set], [Unset], [Fill], or [Clear] will take effect.
Apply(kind CapType) error
}
// NewPid initializes a new [Capabilities] object for given pid when
// it is nonzero, or for the current process if pid is 0.
//
// Deprecated: replace with [NewPid2] followed by optional [Capabilities.Load]
// (only if needed). For example, replace:
//
// c, err := NewPid(0)
// if err != nil {
// return err
// }
//
// with:
//
// c, err := NewPid2(0)
// if err != nil {
// return err
// }
// err = c.Load()
// if err != nil {
// return err
// }
func NewPid(pid int) (Capabilities, error) {
c, err := newPid(pid)
if err != nil {
return c, err
}
err = c.Load()
return c, err
}
// NewPid2 initializes a new [Capabilities] object for given pid when
// it is nonzero, or for the current process if pid is 0. This
// does not load the process's current capabilities; if needed,
// call [Capabilities.Load].
func NewPid2(pid int) (Capabilities, error) {
return newPid(pid)
}
// NewFile initializes a new Capabilities object for given file path.
//
// Deprecated: replace with [NewFile2] followed by optional [Capabilities.Load]
// (only if needed). For example, replace:
//
// c, err := NewFile(path)
// if err != nil {
// return err
// }
//
// with:
//
// c, err := NewFile2(path)
// if err != nil {
// return err
// }
// err = c.Load()
// if err != nil {
// return err
// }
func NewFile(path string) (Capabilities, error) {
c, err := newFile(path)
if err != nil {
return c, err
}
err = c.Load()
return c, err
}
// NewFile2 creates a new initialized [Capabilities] object for given
// file path. This does not load the process's current capabilities;
// if needed, call [Capabilities.Load].
func NewFile2(path string) (Capabilities, error) {
return newFile(path)
}
// LastCap returns highest valid capability of the running kernel,
// or an error if it can not be obtained.
//
// See also: [ListSupported].
func LastCap() (Cap, error) {
return lastCap()
}
// GetAmbient determines if a specific ambient capability is raised in the
// calling thread.
func GetAmbient(c Cap) (bool, error) {
return getAmbient(c)
}
// SetAmbient raises or lowers specified ambient capabilities for the calling
// thread. To complete successfully, the prevailing effective capability set
// must have a raised CAP_SETPCAP. Further, to raise a specific ambient
// capability the inheritable and permitted sets of the calling thread must
// already contain the specified capability.
func SetAmbient(raise bool, caps ...Cap) error {
return setAmbient(raise, caps...)
}
// ResetAmbient resets all of the ambient capabilities for the calling thread
// to their lowered value.
func ResetAmbient() error {
return resetAmbient()
}
// GetBound determines if a specific bounding capability is raised in the
// calling thread.
func GetBound(c Cap) (bool, error) {
return getBound(c)
}
// DropBound lowers the specified bounding set capability.
func DropBound(caps ...Cap) error {
return dropBound(caps...)
}

View File

@ -1,46 +0,0 @@
// Copyright 2023 The Capability Authors.
// Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !linux
package capability
import "errors"
var errNotSup = errors.New("not supported")
func newPid(_ int) (Capabilities, error) {
return nil, errNotSup
}
func newFile(_ string) (Capabilities, error) {
return nil, errNotSup
}
func lastCap() (Cap, error) {
return -1, errNotSup
}
func getAmbient(_ Cap) (bool, error) {
return false, errNotSup
}
func setAmbient(_ bool, _ ...Cap) error {
return errNotSup
}
func resetAmbient() error {
return errNotSup
}
func getBound(_ Cap) (bool, error) {
return false, errNotSup
}
func dropBound(_ ...Cap) error {
return errNotSup
}

View File

@ -51,7 +51,7 @@ func mountedByOpenat2(path string) (bool, error) {
Resolve: unix.RESOLVE_NO_XDEV,
})
_ = unix.Close(dirfd)
switch err {
switch err { //nolint:errorlint // unix errors are bare
case nil: // definitely not a mount
_ = unix.Close(fd)
return false, nil

View File

@ -5,19 +5,15 @@ import (
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"
"sync"
"golang.org/x/sys/unix"
)
// 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/thread-self/mountinfo needs to be read from.
// than /proc/self/mountinfo needs to be read from.
//
// This function is Linux-specific.
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
@ -131,40 +127,8 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
return out, nil
}
var (
haveProcThreadSelf bool
haveProcThreadSelfOnce sync.Once
)
func parseMountTable(filter FilterFunc) (_ []*Info, err error) {
haveProcThreadSelfOnce.Do(func() {
_, err := os.Stat("/proc/thread-self/mountinfo")
haveProcThreadSelf = err == nil
})
// We need to lock ourselves to the current OS thread in order to make sure
// that the thread referenced by /proc/thread-self stays alive until we
// finish parsing the file.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var f *os.File
if haveProcThreadSelf {
f, err = os.Open("/proc/thread-self/mountinfo")
} else {
// On pre-3.17 kernels (such as CentOS 7), we don't have
// /proc/thread-self/ so we need to manually construct
// /proc/self/task/<tid>/ as a fallback.
f, err = os.Open("/proc/self/task/" + strconv.Itoa(unix.Gettid()) + "/mountinfo")
if os.IsNotExist(err) {
// If /proc/self/task/... failed, it means that our active pid
// namespace doesn't match the pid namespace of the /proc mount. In
// this case we just have to make do with /proc/self, since there
// is no other way of figuring out our tid in a parent pid
// namespace on pre-3.17 kernels.
f, err = os.Open("/proc/self/mountinfo")
}
}
func parseMountTable(filter FilterFunc) ([]*Info, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return nil, err
}

View File

@ -1,141 +0,0 @@
package user
import (
"fmt"
"os"
)
// MkdirOpt is a type for options to pass to Mkdir calls
type MkdirOpt func(*mkdirOptions)
type mkdirOptions struct {
onlyNew bool
}
// WithOnlyNew is an option for MkdirAllAndChown that will only change ownership and permissions
// on newly created directories. If the directory already exists, it will not be modified
func WithOnlyNew(o *mkdirOptions) {
o.onlyNew = true
}
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
// ownership to the requested uid/gid. By default, if the directory already exists, this
// function will still change ownership and permissions. If WithOnlyNew is passed as an
// option, then only the newly created directories will have ownership and permissions changed.
func MkdirAllAndChown(path string, mode os.FileMode, uid, gid int, opts ...MkdirOpt) error {
var options mkdirOptions
for _, opt := range opts {
opt(&options)
}
return mkdirAs(path, mode, uid, gid, true, options.onlyNew)
}
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
// By default, if the directory already exists, this function still changes ownership and permissions.
// If WithOnlyNew is passed as an option, then only the newly created directory will have ownership
// and permissions changed.
// Note that unlike os.Mkdir(), this function does not return IsExist error
// in case path already exists.
func MkdirAndChown(path string, mode os.FileMode, uid, gid int, opts ...MkdirOpt) error {
var options mkdirOptions
for _, opt := range opts {
opt(&options)
}
return mkdirAs(path, mode, uid, gid, false, options.onlyNew)
}
// getRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
// If the maps are empty, then the root uid/gid will default to "real" 0/0
func getRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
uid, err := toHost(0, uidMap)
if err != nil {
return -1, -1, err
}
gid, err := toHost(0, gidMap)
if err != nil {
return -1, -1, err
}
return uid, gid, nil
}
// toContainer takes an id mapping, and uses it to translate a
// host ID to the remapped ID. If no map is provided, then the translation
// assumes a 1-to-1 mapping and returns the passed in id
func toContainer(hostID int, idMap []IDMap) (int, error) {
if idMap == nil {
return hostID, nil
}
for _, m := range idMap {
if (int64(hostID) >= m.ParentID) && (int64(hostID) <= (m.ParentID + m.Count - 1)) {
contID := int(m.ID + (int64(hostID) - m.ParentID))
return contID, nil
}
}
return -1, fmt.Errorf("host ID %d cannot be mapped to a container ID", hostID)
}
// toHost takes an id mapping and a remapped ID, and translates the
// ID to the mapped host ID. If no map is provided, then the translation
// assumes a 1-to-1 mapping and returns the passed in id #
func toHost(contID int, idMap []IDMap) (int, error) {
if idMap == nil {
return contID, nil
}
for _, m := range idMap {
if (int64(contID) >= m.ID) && (int64(contID) <= (m.ID + m.Count - 1)) {
hostID := int(m.ParentID + (int64(contID) - m.ID))
return hostID, nil
}
}
return -1, fmt.Errorf("container ID %d cannot be mapped to a host ID", contID)
}
// IdentityMapping contains a mappings of UIDs and GIDs.
// The zero value represents an empty mapping.
type IdentityMapping struct {
UIDMaps []IDMap `json:"UIDMaps"`
GIDMaps []IDMap `json:"GIDMaps"`
}
// RootPair returns a uid and gid pair for the root user. The error is ignored
// because a root user always exists, and the defaults are correct when the uid
// and gid maps are empty.
func (i IdentityMapping) RootPair() (int, int) {
uid, gid, _ := getRootUIDGID(i.UIDMaps, i.GIDMaps)
return uid, gid
}
// ToHost returns the host UID and GID for the container uid, gid.
// Remapping is only performed if the ids aren't already the remapped root ids
func (i IdentityMapping) ToHost(uid, gid int) (int, int, error) {
var err error
ruid, rgid := i.RootPair()
if uid != ruid {
ruid, err = toHost(uid, i.UIDMaps)
if err != nil {
return ruid, rgid, err
}
}
if gid != rgid {
rgid, err = toHost(gid, i.GIDMaps)
}
return ruid, rgid, err
}
// ToContainer returns the container UID and GID for the host uid and gid
func (i IdentityMapping) ToContainer(uid, gid int) (int, int, error) {
ruid, err := toContainer(uid, i.UIDMaps)
if err != nil {
return -1, -1, err
}
rgid, err := toContainer(gid, i.GIDMaps)
return ruid, rgid, err
}
// Empty returns true if there are no id mappings
func (i IdentityMapping) Empty() bool {
return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
}

View File

@ -1,143 +0,0 @@
//go:build !windows
package user
import (
"fmt"
"os"
"path/filepath"
"strconv"
"syscall"
)
func mkdirAs(path string, mode os.FileMode, uid, gid int, mkAll, onlyNew bool) error {
path, err := filepath.Abs(path)
if err != nil {
return err
}
stat, err := os.Stat(path)
if err == nil {
if !stat.IsDir() {
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
}
if onlyNew {
return nil
}
// short-circuit -- we were called with an existing directory and chown was requested
return setPermissions(path, mode, uid, gid, stat)
}
// make an array containing the original path asked for, plus (for mkAll == true)
// all path components leading up to the complete path that don't exist before we MkdirAll
// so that we can chown all of them properly at the end. If onlyNew is true, we won't
// chown the full directory path if it exists
var paths []string
if os.IsNotExist(err) {
paths = append(paths, path)
}
if mkAll {
// walk back to "/" looking for directories which do not exist
// and add them to the paths array for chown after creation
dirPath := path
for {
dirPath = filepath.Dir(dirPath)
if dirPath == "/" {
break
}
if _, err = os.Stat(dirPath); os.IsNotExist(err) {
paths = append(paths, dirPath)
}
}
if err = os.MkdirAll(path, mode); err != nil {
return err
}
} else if err = os.Mkdir(path, mode); err != nil {
return err
}
// even if it existed, we will chown the requested path + any subpaths that
// didn't exist when we called MkdirAll
for _, pathComponent := range paths {
if err = setPermissions(pathComponent, mode, uid, gid, nil); err != nil {
return err
}
}
return nil
}
// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested
// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the
// dir is on an NFS share, so don't call chown unless we absolutely must.
// Likewise for setting permissions.
func setPermissions(p string, mode os.FileMode, uid, gid int, stat os.FileInfo) error {
if stat == nil {
var err error
stat, err = os.Stat(p)
if err != nil {
return err
}
}
if stat.Mode().Perm() != mode.Perm() {
if err := os.Chmod(p, mode.Perm()); err != nil {
return err
}
}
ssi := stat.Sys().(*syscall.Stat_t)
if ssi.Uid == uint32(uid) && ssi.Gid == uint32(gid) {
return nil
}
return os.Chown(p, uid, gid)
}
// LoadIdentityMapping takes a requested username and
// using the data from /etc/sub{uid,gid} ranges, creates the
// proper uid and gid remapping ranges for that user/group pair
func LoadIdentityMapping(name string) (IdentityMapping, error) {
// TODO: Consider adding support for calling out to "getent"
usr, err := LookupUser(name)
if err != nil {
return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %w", name, err)
}
subuidRanges, err := lookupSubRangesFile("/etc/subuid", usr)
if err != nil {
return IdentityMapping{}, err
}
subgidRanges, err := lookupSubRangesFile("/etc/subgid", usr)
if err != nil {
return IdentityMapping{}, err
}
return IdentityMapping{
UIDMaps: subuidRanges,
GIDMaps: subgidRanges,
}, nil
}
func lookupSubRangesFile(path string, usr User) ([]IDMap, error) {
uidstr := strconv.Itoa(usr.Uid)
rangeList, err := ParseSubIDFileFilter(path, func(sid SubID) bool {
return sid.Name == usr.Name || sid.Name == uidstr
})
if err != nil {
return nil, err
}
if len(rangeList) == 0 {
return nil, fmt.Errorf("no subuid ranges found for user %q", usr.Name)
}
idMap := []IDMap{}
var containerID int64
for _, idrange := range rangeList {
idMap = append(idMap, IDMap{
ID: containerID,
ParentID: idrange.SubID,
Count: idrange.Count,
})
containerID = containerID + idrange.Count
}
return idMap, nil
}

View File

@ -1,13 +0,0 @@
package user
import (
"os"
)
// This is currently a wrapper around [os.MkdirAll] since currently
// permissions aren't set through this path, the identity isn't utilized.
// Ownership is handled elsewhere, but in the future could be support here
// too.
func mkdirAs(path string, _ os.FileMode, _, _ int, _, _ bool) error {
return os.MkdirAll(path, 0)
}

View File

@ -176,18 +176,7 @@
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]
Copyright 2014 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

17
vendor/github.com/opencontainers/runc/NOTICE generated vendored Normal file
View File

@ -0,0 +1,17 @@
runc
Copyright 2012-2015 Docker, Inc.
This product includes software developed at Docker, Inc. (http://www.docker.com).
The following is courtesy of our legal counsel:
Use and transfer of Docker may be subject to certain restrictions by the
United States and other governments.
It is your responsibility to ensure that your use and/or transfer does not
violate applicable laws.
For more information, please see http://www.bis.doc.gov
See also http://www.apache.org/dev/crypto.html and/or seek legal counsel.

View File

@ -197,6 +197,7 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
for {
var line []byte
line, isPrefix, err = rd.ReadLine()
if err != nil {
// We should return no error if EOF is reached
// without a match.

Some files were not shown because too many files have changed in this diff Show More