Compare commits
68 Commits
Author | SHA1 | Date |
---|---|---|
|
7c6167725b | |
|
b63cf81bb5 | |
|
e86b33e7c5 | |
|
71fa86684d | |
|
9d8d33bd67 | |
|
bfdd693beb | |
|
2fe6ea2d9f | |
|
4590fdba26 | |
|
a028edf9cb | |
|
22a4f30f48 | |
|
34b21b6dc3 | |
|
63fefe0f20 | |
|
591153cb8f | |
|
8a24390d18 | |
|
97f2983630 | |
|
e2a50af30e | |
|
1a717caef7 | |
|
d363e25d2a | |
|
bdb2268d7b | |
|
c5e4904edc | |
|
630a09f34e | |
|
51eaa377ce | |
|
b8a02cc18f | |
|
6c8ecb124e | |
|
6a2d670f51 | |
|
e9fdef4dbf | |
|
76a55c7ad9 | |
|
e6400f5b96 | |
|
bad4003415 | |
|
a26fed5314 | |
|
1a9b6396ef | |
|
b03cedfb33 | |
|
1326c0606a | |
|
898f7b190e | |
|
3b4191616c | |
|
ff7d010baf | |
|
7af78de4f7 | |
|
00e2d7245d | |
|
f72ed843c0 | |
|
0bc7da7ab1 | |
|
51015d1966 | |
|
f9b9e536f8 | |
|
2f5237395e | |
|
b897a7d464 | |
|
7a6c9d3a3d | |
|
86b2c1c2cc | |
|
266bdfe05f | |
|
1ec8ce61a5 | |
|
ba91441159 | |
|
5553f90b1c | |
|
681179f1bd | |
|
b486e8181e | |
|
cd907412d5 | |
|
969537eb7a | |
|
ec31ff1e86 | |
|
fe7a74ceed | |
|
01ca720a78 | |
|
57e5d60a3f | |
|
062cbb9697 | |
|
12a6b5a7ef | |
|
d7918b036c | |
|
8b59acfd3d | |
|
3082eed8f1 | |
|
a4c0f838cd | |
|
fff84e808d | |
|
5ed5002d6c | |
|
ad9b137be0 | |
|
1c78fe1b15 |
|
@ -11,9 +11,8 @@ env:
|
|||
GOCACHE: "${HOME}/.cache/go-build"
|
||||
|
||||
# VM Image built in containers/automation_images
|
||||
IMAGE_SUFFIX: "c20230816t191118z-f38f37d13"
|
||||
IMAGE_SUFFIX: "c20250324t111922z-f41f40d13"
|
||||
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"
|
||||
|
@ -35,7 +34,6 @@ 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]
|
||||
|
@ -50,7 +48,7 @@ meta_task:
|
|||
vendor_task:
|
||||
|
||||
container:
|
||||
image: golang:1.19
|
||||
image: golang:1.23
|
||||
|
||||
script:
|
||||
- make vendor
|
||||
|
@ -71,9 +69,6 @@ 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:
|
||||
|
|
7
Makefile
7
Makefile
|
@ -6,6 +6,7 @@ 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)
|
||||
|
@ -46,7 +47,7 @@ docs:
|
|||
|
||||
.PHONY: binary
|
||||
binary:
|
||||
$(GO_BUILD) -mod=vendor -o bin/oci-seccomp-bpf-hook -ldflags "-X main.version=$(OSBH_VERSION)" $(PROJECT)
|
||||
$(GO_BUILD) -mod=vendor -o $(BUILDDIR)/bin/oci-seccomp-bpf-hook -ldflags "-X main.version=$(OSBH_VERSION)" $(PROJECT)
|
||||
|
||||
.PHONY: validate
|
||||
validate:
|
||||
|
@ -73,7 +74,7 @@ test-unit:
|
|||
.PHONY: install.tools
|
||||
install.tools: .install.golangci-lint .install.md2man
|
||||
|
||||
.install.golangci-lint: VERSION=v1.51.2
|
||||
.install.golangci-lint: VERSION=v1.60.3
|
||||
.install.golangci-lint:
|
||||
curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/$(VERSION)/install.sh | sh -s -- -b ./build $(VERSION)
|
||||
|
||||
|
@ -94,7 +95,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 bin/oci-seccomp-bpf-hook $(DESTDIR)$(HOOK_BIN_DIR)
|
||||
install $(SELINUXOPT) -m 755 $(BUILDDIR)/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
|
||||
|
||||
|
|
7
ebpf.go
7
ebpf.go
|
@ -111,6 +111,13 @@ 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
24
go.mod
|
@ -1,26 +1,28 @@
|
|||
module github.com/containers/oci-seccomp-bpf-hook
|
||||
|
||||
go 1.19
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.8
|
||||
|
||||
require (
|
||||
github.com/containers/common v0.56.0
|
||||
github.com/containers/storage v1.50.2
|
||||
github.com/containers/common v0.62.3
|
||||
github.com/containers/storage v1.58.0
|
||||
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4
|
||||
github.com/opencontainers/runtime-spec v1.1.0
|
||||
github.com/opencontainers/runtime-spec v1.2.1
|
||||
github.com/seccomp/libseccomp-golang v0.10.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.10.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // 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
|
||||
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
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
40
go.sum
40
go.sum
|
@ -1,11 +1,12 @@
|
|||
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/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/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=
|
||||
|
@ -17,16 +18,20 @@ 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/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/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/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=
|
||||
|
@ -35,14 +40,13 @@ 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.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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/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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/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=
|
||||
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=
|
||||
|
|
|
@ -228,6 +228,13 @@ 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 {
|
||||
|
@ -247,13 +254,6 @@ 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
|
||||
|
|
|
@ -51,9 +51,16 @@ 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",
|
||||
|
@ -68,9 +75,9 @@ func DefaultProfile() *Seccomp {
|
|||
"pciconfig_write",
|
||||
"sgetmask",
|
||||
"ssetmask",
|
||||
"swapcontext",
|
||||
"swapoff",
|
||||
"swapon",
|
||||
"syscall",
|
||||
"sysfs",
|
||||
"uselib",
|
||||
"userfaultfd",
|
||||
|
@ -142,6 +149,7 @@ func DefaultProfile() *Seccomp {
|
|||
"fchdir",
|
||||
"fchmod",
|
||||
"fchmodat",
|
||||
"fchmodat2",
|
||||
"fchown",
|
||||
"fchown32",
|
||||
"fchownat",
|
||||
|
@ -309,7 +317,6 @@ func DefaultProfile() *Seccomp {
|
|||
"pwritev2",
|
||||
"read",
|
||||
"readahead",
|
||||
"readdir",
|
||||
"readlink",
|
||||
"readlinkat",
|
||||
"readv",
|
||||
|
@ -397,15 +404,12 @@ func DefaultProfile() *Seccomp {
|
|||
"shmdt",
|
||||
"shmget",
|
||||
"shutdown",
|
||||
"sigaction",
|
||||
"sigaltstack",
|
||||
"signal",
|
||||
"signalfd",
|
||||
"signalfd4",
|
||||
"sigpending",
|
||||
"sigprocmask",
|
||||
"sigreturn",
|
||||
"sigsuspend",
|
||||
"socketcall",
|
||||
"socketpair",
|
||||
"splice",
|
||||
|
@ -419,7 +423,6 @@ func DefaultProfile() *Seccomp {
|
|||
"sync",
|
||||
"sync_file_range",
|
||||
"syncfs",
|
||||
"syscall",
|
||||
"sysinfo",
|
||||
"syslog",
|
||||
"tee",
|
||||
|
@ -432,7 +435,6 @@ func DefaultProfile() *Seccomp {
|
|||
"timer_gettime64",
|
||||
"timer_settime",
|
||||
"timer_settime64",
|
||||
"timerfd",
|
||||
"timerfd_create",
|
||||
"timerfd_gettime",
|
||||
"timerfd_gettime64",
|
||||
|
@ -522,6 +524,7 @@ func DefaultProfile() *Seccomp {
|
|||
{
|
||||
Names: []string{
|
||||
"sync_file_range2",
|
||||
"swapcontext",
|
||||
},
|
||||
Action: ActAllow,
|
||||
Args: []*Arg{},
|
||||
|
@ -576,6 +579,16 @@ 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",
|
||||
|
@ -603,8 +616,8 @@ func DefaultProfile() *Seccomp {
|
|||
"bpf",
|
||||
"fanotify_init",
|
||||
"lookup_dcookie",
|
||||
"perf_event_open",
|
||||
"quotactl",
|
||||
"quotactl_fd",
|
||||
"setdomainname",
|
||||
"sethostname",
|
||||
"setns",
|
||||
|
@ -617,11 +630,11 @@ func DefaultProfile() *Seccomp {
|
|||
},
|
||||
{
|
||||
Names: []string{
|
||||
"bpf",
|
||||
"fanotify_init",
|
||||
"lookup_dcookie",
|
||||
"perf_event_open",
|
||||
"quotactl",
|
||||
"quotactl_fd",
|
||||
"setdomainname",
|
||||
"sethostname",
|
||||
"setns",
|
||||
|
@ -884,6 +897,50 @@ 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{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux && seccomp
|
||||
// +build linux,seccomp
|
||||
|
||||
package seccomp
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//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
|
||||
|
|
|
@ -55,9 +55,16 @@
|
|||
{
|
||||
"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",
|
||||
|
@ -72,9 +79,9 @@
|
|||
"pciconfig_write",
|
||||
"sgetmask",
|
||||
"ssetmask",
|
||||
"swapcontext",
|
||||
"swapoff",
|
||||
"swapon",
|
||||
"syscall",
|
||||
"sysfs",
|
||||
"uselib",
|
||||
"userfaultfd",
|
||||
|
@ -149,6 +156,7 @@
|
|||
"fchdir",
|
||||
"fchmod",
|
||||
"fchmodat",
|
||||
"fchmodat2",
|
||||
"fchown",
|
||||
"fchown32",
|
||||
"fchownat",
|
||||
|
@ -316,7 +324,6 @@
|
|||
"pwritev2",
|
||||
"read",
|
||||
"readahead",
|
||||
"readdir",
|
||||
"readlink",
|
||||
"readlinkat",
|
||||
"readv",
|
||||
|
@ -404,15 +411,12 @@
|
|||
"shmdt",
|
||||
"shmget",
|
||||
"shutdown",
|
||||
"sigaction",
|
||||
"sigaltstack",
|
||||
"signal",
|
||||
"signalfd",
|
||||
"signalfd4",
|
||||
"sigpending",
|
||||
"sigprocmask",
|
||||
"sigreturn",
|
||||
"sigsuspend",
|
||||
"socketcall",
|
||||
"socketpair",
|
||||
"splice",
|
||||
|
@ -426,7 +430,6 @@
|
|||
"sync",
|
||||
"sync_file_range",
|
||||
"syncfs",
|
||||
"syscall",
|
||||
"sysinfo",
|
||||
"syslog",
|
||||
"tee",
|
||||
|
@ -439,7 +442,6 @@
|
|||
"timer_gettime64",
|
||||
"timer_settime",
|
||||
"timer_settime64",
|
||||
"timerfd",
|
||||
"timerfd_create",
|
||||
"timerfd_gettime",
|
||||
"timerfd_gettime64",
|
||||
|
@ -561,7 +563,8 @@
|
|||
},
|
||||
{
|
||||
"names": [
|
||||
"sync_file_range2"
|
||||
"sync_file_range2",
|
||||
"swapcontext"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [],
|
||||
|
@ -641,6 +644,20 @@
|
|||
},
|
||||
"excludes": {}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"riscv_flush_icache"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [],
|
||||
"comment": "",
|
||||
"includes": {
|
||||
"arches": [
|
||||
"riscv64"
|
||||
]
|
||||
},
|
||||
"excludes": {}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"open_by_handle_at"
|
||||
|
@ -676,8 +693,8 @@
|
|||
"bpf",
|
||||
"fanotify_init",
|
||||
"lookup_dcookie",
|
||||
"perf_event_open",
|
||||
"quotactl",
|
||||
"quotactl_fd",
|
||||
"setdomainname",
|
||||
"sethostname",
|
||||
"setns"
|
||||
|
@ -694,11 +711,11 @@
|
|||
},
|
||||
{
|
||||
"names": [
|
||||
"bpf",
|
||||
"fanotify_init",
|
||||
"lookup_dcookie",
|
||||
"perf_event_open",
|
||||
"quotactl",
|
||||
"quotactl_fd",
|
||||
"setdomainname",
|
||||
"sethostname",
|
||||
"setns"
|
||||
|
@ -1046,6 +1063,68 @@
|
|||
]
|
||||
},
|
||||
"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": {}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
//go:build seccomp
|
||||
// +build seccomp
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux || !seccomp
|
||||
// +build !linux !seccomp
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
@ -15,12 +14,12 @@ import (
|
|||
|
||||
var errNotSupported = errors.New("seccomp not enabled in this build")
|
||||
|
||||
// LoadProfile returns an error on unsuppored systems
|
||||
// LoadProfile returns an error on unsupported systems
|
||||
func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
||||
return nil, errNotSupported
|
||||
}
|
||||
|
||||
// GetDefaultProfile returns an error on unsuppored systems
|
||||
// GetDefaultProfile returns an error on unsupported systems
|
||||
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
||||
return nil, errNotSupported
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux && seccomp
|
||||
// +build linux,seccomp
|
||||
|
||||
package seccomp
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build seccomp
|
||||
// +build seccomp
|
||||
|
||||
package seccomp
|
||||
|
||||
|
|
38
vendor/github.com/containers/storage/pkg/fileutils/exists_freebsd.go
generated
vendored
Normal file
38
vendor/github.com/containers/storage/pkg/fileutils/exists_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//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
|
||||
}
|
18
vendor/github.com/containers/storage/pkg/fileutils/exists_windows.go
generated
vendored
Normal file
18
vendor/github.com/containers/storage/pkg/fileutils/exists_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
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
|
||||
}
|
27
vendor/github.com/containers/storage/pkg/fileutils/fileutils_darwin.go
generated
vendored
Normal file
27
vendor/github.com/containers/storage/pkg/fileutils/fileutils_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
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
|
||||
}
|
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_solaris.go
generated
vendored
Normal file
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
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
|
||||
}
|
21
vendor/github.com/containers/storage/pkg/fileutils/fileutils_unix.go
generated
vendored
Normal file
21
vendor/github.com/containers/storage/pkg/fileutils/fileutils_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
//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
|
||||
}
|
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_windows.go
generated
vendored
Normal file
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package fileutils
|
||||
|
||||
// GetTotalUsedFds Returns the number of used File Descriptors. Not supported
|
||||
// on Windows.
|
||||
func GetTotalUsedFds() int {
|
||||
return -1
|
||||
}
|
20
vendor/github.com/containers/storage/pkg/fileutils/reflink_linux.go
generated
vendored
Normal file
20
vendor/github.com/containers/storage/pkg/fileutils/reflink_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
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
|
||||
}
|
15
vendor/github.com/containers/storage/pkg/fileutils/reflink_unsupported.go
generated
vendored
Normal file
15
vendor/github.com/containers/storage/pkg/fileutils/reflink_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
//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
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
|
@ -228,7 +229,7 @@ func getOverflowUID() int {
|
|||
return overflowUID
|
||||
}
|
||||
|
||||
// getOverflowUID returns the GID mapped to the overflow user
|
||||
// getOverflowGID 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
|
||||
|
@ -367,21 +368,174 @@ 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" {
|
||||
var mode uint64 = 0o0700
|
||||
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
|
||||
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
|
||||
}
|
||||
}
|
||||
stat := Stat{
|
||||
Mode: os.FileMode(0o0700),
|
||||
}
|
||||
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
|
||||
if err = system.Lsetxattr(name, ContainersOverrideXattr, []byte(value), 0); err != nil {
|
||||
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
|
||||
if err == nil && xstat != nil {
|
||||
stat, err = parseOverrideXattr(xstat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} 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 {
|
||||
return err
|
||||
}
|
||||
uid = os.Getuid()
|
||||
|
@ -397,19 +551,24 @@ func SafeChown(name string, uid, gid int) error {
|
|||
|
||||
func SafeLchown(name string, uid, gid int) error {
|
||||
if runtime.GOOS == "darwin" {
|
||||
var mode uint64 = 0o0700
|
||||
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
|
||||
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
|
||||
}
|
||||
}
|
||||
stat := Stat{
|
||||
Mode: os.FileMode(0o0700),
|
||||
}
|
||||
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
|
||||
if err = system.Lsetxattr(name, ContainersOverrideXattr, []byte(value), 0); err != nil {
|
||||
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
|
||||
if err == nil && xstat != nil {
|
||||
stat, err = parseOverrideXattr(xstat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} 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 {
|
||||
return err
|
||||
}
|
||||
uid = os.Getuid()
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//go:build linux && cgo && libsubid
|
||||
// +build linux,cgo,libsubid
|
||||
|
||||
package idtools
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/user"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -14,16 +14,14 @@ 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];
|
||||
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
|
||||
|
@ -31,6 +29,8 @@ 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,6 +43,10 @@ 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))
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package idtools
|
||||
|
||||
|
@ -13,8 +12,9 @@ import (
|
|||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/containers/storage/pkg/fileutils"
|
||||
"github.com/containers/storage/pkg/system"
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
"github.com/moby/sys/user"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -55,7 +55,7 @@ func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chown
|
|||
if dirPath == "/" {
|
||||
break
|
||||
}
|
||||
if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) {
|
||||
if err := fileutils.Exists(dirPath); err != nil && os.IsNotExist(err) {
|
||||
paths = append(paths, dirPath)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux || !libsubid || !cgo
|
||||
// +build !linux !libsubid !cgo
|
||||
|
||||
package idtools
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package idtools
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package idtools
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package idtools
|
||||
|
||||
|
|
|
@ -97,14 +97,14 @@ func MergeTmpfsOptions(options []string) ([]string, error) {
|
|||
}
|
||||
continue
|
||||
}
|
||||
opt := strings.SplitN(option, "=", 2)
|
||||
if len(opt) != 2 || !validFlags[opt[0]] {
|
||||
opt, _, ok := strings.Cut(option, "=")
|
||||
if !ok || !validFlags[opt] {
|
||||
return nil, fmt.Errorf("invalid tmpfs option %q", opt)
|
||||
}
|
||||
if !dataCollisions[opt[0]] {
|
||||
if !dataCollisions[opt] {
|
||||
// We prepend the option and add to collision map
|
||||
newOptions = append([]string{option}, newOptions...)
|
||||
dataCollisions[opt[0]] = true
|
||||
dataCollisions[opt] = 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.SplitN(o, "=", 2)
|
||||
if !validFlags[opt[0]] {
|
||||
opt, _, _ := strings.Cut(o, "=")
|
||||
if !validFlags[opt] {
|
||||
return 0, "", fmt.Errorf("invalid tmpfs option %q", opt)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux && !freebsd
|
||||
// +build !linux,!freebsd
|
||||
|
||||
package mount
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build freebsd && cgo
|
||||
// +build freebsd,cgo
|
||||
|
||||
package mount
|
||||
|
||||
|
@ -40,13 +39,9 @@ func mount(device, target, mType string, flag uintptr, data string) error {
|
|||
isNullFS = true
|
||||
continue
|
||||
}
|
||||
opt := strings.SplitN(x, "=", 2)
|
||||
options = append(options, opt[0])
|
||||
if len(opt) == 2 {
|
||||
options = append(options, opt[1])
|
||||
} else {
|
||||
options = append(options, "")
|
||||
}
|
||||
name, val, _ := strings.Cut(x, "=")
|
||||
options = append(options, name)
|
||||
options = append(options, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//go:build !linux && !(freebsd && cgo)
|
||||
// +build !linux
|
||||
// +build !freebsd !cgo
|
||||
|
||||
package mount
|
||||
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
package mount
|
||||
|
||||
import "github.com/moby/sys/mountinfo"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
var PidMountInfo = mountinfo.PidMountInfo
|
||||
"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)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package mount
|
||||
|
||||
|
@ -11,7 +10,7 @@ import (
|
|||
|
||||
func unmount(target string, flags int) error {
|
||||
var err error
|
||||
for i := 0; i < 50; i++ {
|
||||
for range 50 {
|
||||
err = unix.Unmount(target, flags)
|
||||
switch err {
|
||||
case unix.EBUSY:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package mount
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package reexec
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package reexec
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build solaris || darwin
|
||||
// +build solaris darwin
|
||||
|
||||
package reexec
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux && !windows && !freebsd && !solaris && !darwin
|
||||
// +build !linux,!windows,!freebsd,!solaris,!darwin
|
||||
|
||||
package reexec
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package reexec
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ func panicIfNotInitialized() {
|
|||
}
|
||||
}
|
||||
|
||||
func naiveSelf() string { //nolint: unused
|
||||
func naiveSelf() string {
|
||||
name := os.Args[0]
|
||||
if filepath.Base(name) == name {
|
||||
if lp, err := exec.LookPath(name); err == nil {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !regexp_precompile
|
||||
// +build !regexp_precompile
|
||||
|
||||
package regexp
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build regexp_precompile
|
||||
// +build regexp_precompile
|
||||
|
||||
package regexp
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
93
vendor/github.com/containers/storage/pkg/system/extattr_freebsd.go
generated
vendored
Normal file
93
vendor/github.com/containers/storage/pkg/system/extattr_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
//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
|
||||
}
|
24
vendor/github.com/containers/storage/pkg/system/extattr_unsupported.go
generated
vendored
Normal file
24
vendor/github.com/containers/storage/pkg/system/extattr_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
//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
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build freebsd && cgo
|
||||
// +build freebsd,cgo
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build solaris && cgo
|
||||
// +build solaris,cgo
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
//go:build !linux && !windows && !solaris && !(freebsd && cgo)
|
||||
// +build !linux
|
||||
// +build !windows
|
||||
// +build !solaris
|
||||
// +build !freebsd !cgo
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows && !freebsd
|
||||
// +build !windows,!freebsd
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux || freebsd || solaris || darwin
|
||||
// +build linux freebsd solaris darwin
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ func EnsureRemoveAll(dir string) error {
|
|||
|
||||
// track retries
|
||||
exitOnErr := make(map[string]int)
|
||||
maxRetry := 100
|
||||
maxRetry := 1000
|
||||
|
||||
// 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("RecusiveUnmount on %s failed: %v", dir, err)
|
||||
logrus.Debugf("RecursiveUnmount on %s failed: %v", dir, err)
|
||||
}
|
||||
|
||||
for {
|
||||
|
@ -94,6 +94,6 @@ func EnsureRemoveAll(dir string) error {
|
|||
return err
|
||||
}
|
||||
exitOnErr[pe.Path]++
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !freebsd
|
||||
// +build !freebsd
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !freebsd
|
||||
// +build !freebsd
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -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),
|
||||
rdev: uint64(s.Rdev), //nolint:unconvert
|
||||
mtim: s.Mtim,
|
||||
dev: uint64(s.Dev),
|
||||
dev: uint64(s.Dev), //nolint:unconvert
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
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
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux || freebsd || darwin
|
||||
// +build linux freebsd darwin
|
||||
//go:build !windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux && !freebsd
|
||||
// +build !linux,!freebsd
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ const (
|
|||
E2BIG unix.Errno = unix.E2BIG
|
||||
|
||||
// Operation not supported
|
||||
EOPNOTSUPP unix.Errno = unix.EOPNOTSUPP
|
||||
ENOTSUP unix.Errno = unix.ENOTSUP
|
||||
)
|
||||
|
||||
// Lgetxattr retrieves the value of the extended attribute identified by attr
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
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
|
||||
}
|
|
@ -12,7 +12,7 @@ const (
|
|||
E2BIG unix.Errno = unix.E2BIG
|
||||
|
||||
// Operation not supported
|
||||
EOPNOTSUPP unix.Errno = unix.EOPNOTSUPP
|
||||
ENOTSUP unix.Errno = unix.ENOTSUP
|
||||
|
||||
// Value is too small or too large for maximum size allowed
|
||||
EOVERFLOW unix.Errno = unix.EOVERFLOW
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux && !darwin
|
||||
// +build !linux,!darwin
|
||||
//go:build !linux && !darwin && !freebsd
|
||||
|
||||
package system
|
||||
|
||||
|
@ -10,7 +9,7 @@ const (
|
|||
E2BIG syscall.Errno = syscall.Errno(0)
|
||||
|
||||
// Operation not supported
|
||||
EOPNOTSUPP syscall.Errno = syscall.Errno(0)
|
||||
ENOTSUP syscall.Errno = syscall.Errno(0)
|
||||
|
||||
// Value is too small or too large for maximum size allowed
|
||||
EOVERFLOW syscall.Errno = syscall.Errno(0)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux && cgo
|
||||
// +build linux,cgo
|
||||
|
||||
package unshare
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux && !cgo
|
||||
// +build linux,!cgo
|
||||
|
||||
package unshare
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <sys/mount.h>
|
||||
#include <linux/limits.h>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build (linux && cgo && !gccgo) || (freebsd && cgo)
|
||||
// +build linux,cgo,!gccgo freebsd,cgo
|
||||
|
||||
package unshare
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package unshare
|
||||
|
||||
|
@ -25,6 +24,11 @@ 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+"=")
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package unshare
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux && cgo && gccgo
|
||||
// +build linux,cgo,gccgo
|
||||
|
||||
package unshare
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package unshare
|
||||
|
||||
|
@ -21,9 +20,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
|
||||
|
@ -33,9 +32,9 @@ type Cmd struct {
|
|||
*exec.Cmd
|
||||
UnshareFlags int
|
||||
UseNewuidmap bool
|
||||
UidMappings []specs.LinuxIDMapping // nolint: revive,golint
|
||||
UidMappings []specs.LinuxIDMapping //nolint: revive
|
||||
UseNewgidmap bool
|
||||
GidMappings []specs.LinuxIDMapping // nolint: revive,golint
|
||||
GidMappings []specs.LinuxIDMapping //nolint: revive
|
||||
GidMappingsEnableSetgroups bool
|
||||
Setsid bool
|
||||
Setpgrp bool
|
||||
|
@ -99,7 +98,7 @@ func IsSetID(path string, modeid os.FileMode, capid capability.Cap) (bool, error
|
|||
return cap.Get(capability.EFFECTIVE, capid), nil
|
||||
}
|
||||
|
||||
func (c *Cmd) Start() error {
|
||||
func (c *Cmd) Start() (retErr error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
|
@ -168,6 +167,15 @@ func (c *Cmd) Start() 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
|
||||
|
@ -241,7 +249,7 @@ func (c *Cmd) Start() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("finding newgidmap: %w", err)
|
||||
}
|
||||
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(strings.Replace(g.String(), "\n", " ", -1))...)...)
|
||||
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(g.String())...)...)
|
||||
g.Reset()
|
||||
cmd.Stdout = g
|
||||
cmd.Stderr = g
|
||||
|
@ -259,7 +267,7 @@ func (c *Cmd) Start() error {
|
|||
}
|
||||
logrus.Warnf("Falling back to single mapping")
|
||||
g.Reset()
|
||||
g.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Getegid())))
|
||||
fmt.Fprintf(g, "0 %d 1\n", os.Getegid())
|
||||
}
|
||||
}
|
||||
if !gidmapSet {
|
||||
|
@ -301,7 +309,7 @@ func (c *Cmd) Start() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("finding newuidmap: %w", err)
|
||||
}
|
||||
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(strings.Replace(u.String(), "\n", " ", -1))...)...)
|
||||
cmd := exec.Command(path, append([]string{pidString}, strings.Fields(u.String())...)...)
|
||||
u.Reset()
|
||||
cmd.Stdout = u
|
||||
cmd.Stderr = u
|
||||
|
@ -320,7 +328,7 @@ func (c *Cmd) Start() error {
|
|||
|
||||
logrus.Warnf("Falling back to single mapping")
|
||||
u.Reset()
|
||||
u.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Geteuid())))
|
||||
fmt.Fprintf(u, "0 %d 1\n", os.Geteuid())
|
||||
}
|
||||
}
|
||||
if !uidmapSet {
|
||||
|
@ -441,6 +449,16 @@ 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")
|
||||
|
@ -450,7 +468,7 @@ type Runnable interface {
|
|||
Run() error
|
||||
}
|
||||
|
||||
func bailOnError(err error, format string, a ...interface{}) { // nolint: revive,goprintffuncname
|
||||
func bailOnError(err error, format string, a ...any) { //nolint:revive,goprintffuncname
|
||||
if err != nil {
|
||||
if format != "" {
|
||||
logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
|
||||
|
@ -516,8 +534,11 @@ 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.NewPid(0)
|
||||
capabilities, err := capability.NewPid2(0)
|
||||
bailOnError(err, "Initializing a new Capabilities object of pid 0")
|
||||
err = capabilities.Load()
|
||||
bailOnError(err, "Reading the current capabilities sets")
|
||||
|
||||
if capabilities.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) {
|
||||
return
|
||||
}
|
||||
|
@ -577,7 +598,12 @@ func MaybeReexecUsingUserNamespace(evenForRoot bool) {
|
|||
cmd.Hook = func(int) error {
|
||||
go func() {
|
||||
for receivedSignal := range interrupted {
|
||||
cmd.Cmd.Process.Signal(receivedSignal)
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !linux && !darwin
|
||||
// +build !linux,!darwin
|
||||
|
||||
package unshare
|
||||
|
||||
|
@ -17,7 +16,7 @@ const (
|
|||
|
||||
// IsRootless tells us if we are running in rootless mode
|
||||
func IsRootless() bool {
|
||||
return false
|
||||
return os.Getuid() != 0
|
||||
}
|
||||
|
||||
// GetRootlessUID returns the UID of the user in the parent userNS
|
||||
|
@ -25,6 +24,11 @@ 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+"=")
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//go:build cgo && !(linux || freebsd)
|
||||
// +build cgo,!linux,!freebsd
|
||||
|
||||
package unshare
|
||||
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
# 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
|
|
@ -1,3 +1,4 @@
|
|||
Copyright 2023 The Capability Authors.
|
||||
Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
|
||||
All rights reserved.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
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).
|
||||
|
||||
[](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
|
|
@ -0,0 +1,176 @@
|
|||
// 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...)
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// 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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package capability
|
||||
|
||||
|
@ -12,62 +13,53 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var errUnknownVers = errors.New("unknown capability version")
|
||||
|
||||
const (
|
||||
linuxCapVer1 = 0x19980330
|
||||
linuxCapVer2 = 0x20071026
|
||||
linuxCapVer1 = 0x19980330 // No longer supported.
|
||||
linuxCapVer2 = 0x20071026 // No longer supported.
|
||||
linuxCapVer3 = 0x20080522
|
||||
)
|
||||
|
||||
var (
|
||||
capVers uint32
|
||||
capLastCap Cap
|
||||
)
|
||||
|
||||
func init() {
|
||||
var hdr capHeader
|
||||
capget(&hdr, nil)
|
||||
capVers = hdr.version
|
||||
|
||||
if initLastCap() == nil {
|
||||
CAP_LAST_CAP = capLastCap
|
||||
if capLastCap > 31 {
|
||||
capUpperMask = (uint32(1) << (uint(capLastCap) - 31)) - 1
|
||||
} else {
|
||||
capUpperMask = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initLastCap() error {
|
||||
if capLastCap != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var lastCap = sync.OnceValues(func() (Cap, error) {
|
||||
f, err := os.Open("/proc/sys/kernel/cap_last_cap")
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var b []byte = make([]byte, 11)
|
||||
_, err = f.Read(b)
|
||||
buf := make([]byte, 11)
|
||||
l, err := f.Read(buf)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
buf = buf[:l]
|
||||
|
||||
fmt.Sscanf(string(b), "%d", &capLastCap)
|
||||
last, err := strconv.Atoi(strings.TrimSpace(string(buf)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return Cap(last), nil
|
||||
})
|
||||
|
||||
return nil
|
||||
func capUpperMask() uint32 {
|
||||
last, err := lastCap()
|
||||
if err != nil || last < 32 {
|
||||
return 0
|
||||
}
|
||||
return (uint32(1) << (uint(last) - 31)) - 1
|
||||
}
|
||||
|
||||
func mkStringCap(c Capabilities, which CapType) (ret string) {
|
||||
for i, first := Cap(0), true; i <= CAP_LAST_CAP; i++ {
|
||||
last, err := lastCap()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for i, first := Cap(0), true; i <= last; i++ {
|
||||
if !c.Get(which, i) {
|
||||
continue
|
||||
}
|
||||
|
@ -98,136 +90,38 @@ func mkString(c Capabilities, max CapType) (ret string) {
|
|||
return
|
||||
}
|
||||
|
||||
func newPid(pid int) (c Capabilities, err error) {
|
||||
switch capVers {
|
||||
case linuxCapVer1:
|
||||
p := new(capsV1)
|
||||
p.hdr.version = capVers
|
||||
p.hdr.pid = int32(pid)
|
||||
c = p
|
||||
case linuxCapVer2, linuxCapVer3:
|
||||
p := new(capsV3)
|
||||
p.hdr.version = capVers
|
||||
p.hdr.pid = int32(pid)
|
||||
c = p
|
||||
default:
|
||||
err = errUnknownVers
|
||||
var capVersion = sync.OnceValues(func() (uint32, error) {
|
||||
var hdr capHeader
|
||||
err := capget(&hdr, nil)
|
||||
return hdr.version, err
|
||||
})
|
||||
|
||||
func newPid(pid int) (c Capabilities, retErr error) {
|
||||
ver, err := capVersion()
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("unable to get capability version from the kernel: %w", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type capsV1 struct {
|
||||
hdr capHeader
|
||||
data capData
|
||||
}
|
||||
|
||||
func (c *capsV1) Get(which CapType, what Cap) bool {
|
||||
if what > 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch which {
|
||||
case EFFECTIVE:
|
||||
return (1<<uint(what))&c.data.effective != 0
|
||||
case PERMITTED:
|
||||
return (1<<uint(what))&c.data.permitted != 0
|
||||
case INHERITABLE:
|
||||
return (1<<uint(what))&c.data.inheritable != 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *capsV1) getData(which CapType) (ret uint32) {
|
||||
switch which {
|
||||
case EFFECTIVE:
|
||||
ret = c.data.effective
|
||||
case PERMITTED:
|
||||
ret = c.data.permitted
|
||||
case INHERITABLE:
|
||||
ret = c.data.inheritable
|
||||
switch ver {
|
||||
case linuxCapVer1, linuxCapVer2:
|
||||
retErr = errors.New("old/unsupported capability version (kernel older than 2.6.26?)")
|
||||
default:
|
||||
// Either linuxCapVer3, or an unknown/future version (such as v4).
|
||||
// In the latter case, we fall back to v3 as the latest version known
|
||||
// to this package, as kernel should be backward-compatible to v3.
|
||||
p := new(capsV3)
|
||||
p.hdr.version = linuxCapVer3
|
||||
p.hdr.pid = int32(pid)
|
||||
c = p
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *capsV1) Empty(which CapType) bool {
|
||||
return c.getData(which) == 0
|
||||
}
|
||||
|
||||
func (c *capsV1) Full(which CapType) bool {
|
||||
return (c.getData(which) & 0x7fffffff) == 0x7fffffff
|
||||
}
|
||||
|
||||
func (c *capsV1) Set(which CapType, caps ...Cap) {
|
||||
for _, what := range caps {
|
||||
if what > 32 {
|
||||
continue
|
||||
}
|
||||
|
||||
if which&EFFECTIVE != 0 {
|
||||
c.data.effective |= 1 << uint(what)
|
||||
}
|
||||
if which&PERMITTED != 0 {
|
||||
c.data.permitted |= 1 << uint(what)
|
||||
}
|
||||
if which&INHERITABLE != 0 {
|
||||
c.data.inheritable |= 1 << uint(what)
|
||||
}
|
||||
func ignoreEINVAL(err error) error {
|
||||
if errors.Is(err, syscall.EINVAL) {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *capsV1) Unset(which CapType, caps ...Cap) {
|
||||
for _, what := range caps {
|
||||
if what > 32 {
|
||||
continue
|
||||
}
|
||||
|
||||
if which&EFFECTIVE != 0 {
|
||||
c.data.effective &= ^(1 << uint(what))
|
||||
}
|
||||
if which&PERMITTED != 0 {
|
||||
c.data.permitted &= ^(1 << uint(what))
|
||||
}
|
||||
if which&INHERITABLE != 0 {
|
||||
c.data.inheritable &= ^(1 << uint(what))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *capsV1) Fill(kind CapType) {
|
||||
if kind&CAPS == CAPS {
|
||||
c.data.effective = 0x7fffffff
|
||||
c.data.permitted = 0x7fffffff
|
||||
c.data.inheritable = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (c *capsV1) Clear(kind CapType) {
|
||||
if kind&CAPS == CAPS {
|
||||
c.data.effective = 0
|
||||
c.data.permitted = 0
|
||||
c.data.inheritable = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (c *capsV1) StringCap(which CapType) (ret string) {
|
||||
return mkStringCap(c, which)
|
||||
}
|
||||
|
||||
func (c *capsV1) String() (ret string) {
|
||||
return mkString(c, BOUNDING)
|
||||
}
|
||||
|
||||
func (c *capsV1) Load() (err error) {
|
||||
return capget(&c.hdr, &c.data)
|
||||
}
|
||||
|
||||
func (c *capsV1) Apply(kind CapType) error {
|
||||
if kind&CAPS == CAPS {
|
||||
return capset(&c.hdr, &c.data)
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
type capsV3 struct {
|
||||
|
@ -292,7 +186,8 @@ func (c *capsV3) Full(which CapType) bool {
|
|||
if (data[0] & 0xffffffff) != 0xffffffff {
|
||||
return false
|
||||
}
|
||||
return (data[1] & capUpperMask) == capUpperMask
|
||||
mask := capUpperMask()
|
||||
return (data[1] & mask) == mask
|
||||
}
|
||||
|
||||
func (c *capsV3) Set(which CapType, caps ...Cap) {
|
||||
|
@ -401,15 +296,12 @@ func (c *capsV3) Load() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
var status_path string
|
||||
|
||||
if c.hdr.pid == 0 {
|
||||
status_path = fmt.Sprintf("/proc/self/status")
|
||||
} else {
|
||||
status_path = fmt.Sprintf("/proc/%d/status", c.hdr.pid)
|
||||
path := "/proc/self/status"
|
||||
if c.hdr.pid != 0 {
|
||||
path = fmt.Sprintf("/proc/%d/status", c.hdr.pid)
|
||||
}
|
||||
|
||||
f, err := os.Open(status_path)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -422,12 +314,18 @@ func (c *capsV3) Load() (err error) {
|
|||
}
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(line, "CapB") {
|
||||
fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0])
|
||||
if val, ok := strings.CutPrefix(line, "CapBnd:\t"); ok {
|
||||
_, err = fmt.Sscanf(val, "%08x%08x", &c.bounds[1], &c.bounds[0])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "CapA") {
|
||||
fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0])
|
||||
if val, ok := strings.CutPrefix(line, "CapAmb:\t"); ok {
|
||||
_, err = fmt.Sscanf(val, "%08x%08x", &c.ambient[1], &c.ambient[0])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -436,26 +334,29 @@ func (c *capsV3) Load() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (c *capsV3) Apply(kind CapType) (err error) {
|
||||
func (c *capsV3) Apply(kind CapType) error {
|
||||
if c.hdr.pid != 0 {
|
||||
return errors.New("unable to modify capabilities of another process")
|
||||
}
|
||||
last, err := LastCap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kind&BOUNDS == BOUNDS {
|
||||
var data [2]capData
|
||||
err = capget(&c.hdr, &data[0])
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
if (1<<uint(CAP_SETPCAP))&data[0].effective != 0 {
|
||||
for i := Cap(0); i <= CAP_LAST_CAP; i++ {
|
||||
for i := Cap(0); i <= last; i++ {
|
||||
if c.Get(BOUNDING, i) {
|
||||
continue
|
||||
}
|
||||
err = prctl(syscall.PR_CAPBSET_DROP, uintptr(i), 0, 0, 0)
|
||||
// Ignore EINVAL since the capability may not be supported in this system.
|
||||
err = ignoreEINVAL(dropBound(i))
|
||||
if err != nil {
|
||||
// Ignore EINVAL since the capability may not be supported in this system.
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINVAL {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -464,26 +365,73 @@ func (c *capsV3) Apply(kind CapType) (err error) {
|
|||
if kind&CAPS == CAPS {
|
||||
err = capset(&c.hdr, &c.data[0])
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if kind&AMBS == AMBS {
|
||||
for i := Cap(0); i <= CAP_LAST_CAP; i++ {
|
||||
action := pr_CAP_AMBIENT_LOWER
|
||||
if c.Get(AMBIENT, i) {
|
||||
action = pr_CAP_AMBIENT_RAISE
|
||||
}
|
||||
err := prctl(pr_CAP_AMBIENT, action, uintptr(i), 0, 0)
|
||||
// Ignore EINVAL as not supported on kernels before 4.3
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINVAL {
|
||||
err = nil
|
||||
// Ignore EINVAL as not supported on kernels before 4.3
|
||||
err = ignoreEINVAL(resetAmbient())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := Cap(0); i <= last; i++ {
|
||||
if !c.Get(AMBIENT, i) {
|
||||
continue
|
||||
}
|
||||
// Ignore EINVAL as not supported on kernels before 4.3
|
||||
err = ignoreEINVAL(setAmbient(true, i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAmbient(c Cap) (bool, error) {
|
||||
res, err := prctlRetInt(pr_CAP_AMBIENT, pr_CAP_AMBIENT_IS_SET, uintptr(c))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return res > 0, nil
|
||||
}
|
||||
|
||||
func setAmbient(raise bool, caps ...Cap) error {
|
||||
op := pr_CAP_AMBIENT_RAISE
|
||||
if !raise {
|
||||
op = pr_CAP_AMBIENT_LOWER
|
||||
}
|
||||
for _, val := range caps {
|
||||
err := prctl(pr_CAP_AMBIENT, op, uintptr(val))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetAmbient() error {
|
||||
return prctl(pr_CAP_AMBIENT, pr_CAP_AMBIENT_CLEAR_ALL, 0)
|
||||
}
|
||||
|
||||
func getBound(c Cap) (bool, error) {
|
||||
res, err := prctlRetInt(syscall.PR_CAPBSET_READ, uintptr(c), 0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return res > 0, nil
|
||||
}
|
||||
|
||||
func dropBound(caps ...Cap) error {
|
||||
for _, val := range caps {
|
||||
err := prctl(syscall.PR_CAPBSET_DROP, uintptr(val), 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFile(path string) (c Capabilities, err error) {
|
||||
|
@ -547,7 +495,8 @@ func (c *capsFile) Full(which CapType) bool {
|
|||
if (data[0] & 0xffffffff) != 0xffffffff {
|
||||
return false
|
||||
}
|
||||
return (data[1] & capUpperMask) == capUpperMask
|
||||
mask := capUpperMask()
|
||||
return (data[1] & mask) == mask
|
||||
}
|
||||
|
||||
func (c *capsFile) Set(which CapType, caps ...Cap) {
|
|
@ -0,0 +1,46 @@
|
|||
// 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
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// Copyright 2024 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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package capability
|
||||
|
||||
import "slices"
|
||||
|
||||
type CapType uint
|
||||
|
||||
func (c CapType) String() string {
|
||||
|
@ -301,9 +304,27 @@ const (
|
|||
CAP_CHECKPOINT_RESTORE = Cap(40)
|
||||
)
|
||||
|
||||
var (
|
||||
// Highest valid capability of the running kernel.
|
||||
CAP_LAST_CAP = Cap(63)
|
||||
// List returns the list of all capabilities known to the package.
|
||||
//
|
||||
// Deprecated: use [ListKnown] or [ListSupported] instead.
|
||||
func List() []Cap {
|
||||
return ListKnown()
|
||||
}
|
||||
|
||||
capUpperMask = ^uint32(0)
|
||||
)
|
||||
// ListKnown returns the list of all capabilities known to the package.
|
||||
func ListKnown() []Cap {
|
||||
return list()
|
||||
}
|
||||
|
||||
// ListSupported returns the list of all capabilities known to the package,
|
||||
// except those that are not supported by the currently running Linux kernel.
|
||||
func ListSupported() ([]Cap, error) {
|
||||
last, err := LastCap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return slices.DeleteFunc(list(), func(c Cap) bool {
|
||||
// Remove caps not supported by the kernel.
|
||||
return c > last
|
||||
}), nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// generated file; DO NOT EDIT - use go generate in directory with source
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
|
||||
package capability
|
||||
|
||||
|
@ -90,8 +90,7 @@ func (c Cap) String() string {
|
|||
return "unknown"
|
||||
}
|
||||
|
||||
// List returns list of all supported capabilities
|
||||
func List() []Cap {
|
||||
func list() []Cap {
|
||||
return []Cap{
|
||||
CAP_CHOWN,
|
||||
CAP_DAC_OVERRIDE,
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
|
||||
// Copyright 2024 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.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package capability
|
||||
|
||||
|
@ -23,7 +24,7 @@ type capData struct {
|
|||
}
|
||||
|
||||
func capget(hdr *capHeader, data *capData) (err error) {
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
|
||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
|
@ -31,7 +32,7 @@ func capget(hdr *capHeader, data *capData) (err error) {
|
|||
}
|
||||
|
||||
func capset(hdr *capHeader, data *capData) (err error) {
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
|
||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
|
@ -47,14 +48,22 @@ const (
|
|||
pr_CAP_AMBIENT_CLEAR_ALL = uintptr(4)
|
||||
)
|
||||
|
||||
func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
|
||||
func prctl(option int, arg2, arg3 uintptr) (err error) {
|
||||
_, _, e1 := syscall.RawSyscall(syscall.SYS_PRCTL, uintptr(option), arg2, arg3)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func prctlRetInt(option int, arg2, arg3 uintptr) (int, error) {
|
||||
ret, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, uintptr(option), arg2, arg3)
|
||||
if err != 0 {
|
||||
return 0, err
|
||||
}
|
||||
return int(ret), nil
|
||||
}
|
||||
|
||||
const (
|
||||
vfsXattrName = "security.capability"
|
||||
|
||||
|
@ -79,9 +88,7 @@ type vfscapData struct {
|
|||
version int8
|
||||
}
|
||||
|
||||
var (
|
||||
_vfsXattrName *byte
|
||||
)
|
||||
var _vfsXattrName *byte
|
||||
|
||||
func init() {
|
||||
_vfsXattrName, _ = syscall.BytePtrFromString(vfsXattrName)
|
||||
|
@ -93,7 +100,7 @@ func getVfsCap(path string, dest *vfscapData) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0)
|
||||
r0, _, e1 := syscall.RawSyscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0)
|
||||
if e1 != 0 {
|
||||
if e1 == syscall.ENODATA {
|
||||
dest.version = 2
|
||||
|
@ -146,7 +153,7 @@ func setVfsCap(path string, data *vfscapData) (err error) {
|
|||
} else {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0)
|
||||
_, _, e1 := syscall.RawSyscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
|
@ -51,7 +51,7 @@ func mountedByOpenat2(path string) (bool, error) {
|
|||
Resolve: unix.RESOLVE_NO_XDEV,
|
||||
})
|
||||
_ = unix.Close(dirfd)
|
||||
switch err { //nolint:errorlint // unix errors are bare
|
||||
switch err {
|
||||
case nil: // definitely not a mount
|
||||
_ = unix.Close(fd)
|
||||
return false, nil
|
||||
|
|
|
@ -5,15 +5,19 @@ 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/self/mountinfo needs to be read from.
|
||||
// than /proc/thread-self/mountinfo needs to be read from.
|
||||
//
|
||||
// This function is Linux-specific.
|
||||
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
|
||||
|
@ -127,8 +131,40 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func parseMountTable(filter FilterFunc) ([]*Info, error) {
|
||||
f, err := os.Open("/proc/self/mountinfo")
|
||||
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")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -158,10 +194,10 @@ func PidMountInfo(pid int) ([]*Info, error) {
|
|||
// A few specific characters in mountinfo path entries (root and mountpoint)
|
||||
// are escaped using a backslash followed by a character's ascii code in octal.
|
||||
//
|
||||
// space -- as \040
|
||||
// tab (aka \t) -- as \011
|
||||
// newline (aka \n) -- as \012
|
||||
// backslash (aka \\) -- as \134
|
||||
// space -- as \040
|
||||
// tab (aka \t) -- as \011
|
||||
// newline (aka \n) -- as \012
|
||||
// backslash (aka \\) -- as \134
|
||||
//
|
||||
// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
|
||||
func unescape(path string) (string, error) {
|
||||
|
|
13
vendor/github.com/opencontainers/runc/LICENSE → vendor/github.com/moby/sys/user/LICENSE
generated
vendored
13
vendor/github.com/opencontainers/runc/LICENSE → vendor/github.com/moby/sys/user/LICENSE
generated
vendored
|
@ -176,7 +176,18 @@
|
|||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014 Docker, Inc.
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
|
@ -0,0 +1,141 @@
|
|||
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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue