Compare commits

...

68 Commits

Author SHA1 Message Date
Valentin Rothberg 7c6167725b
Merge pull request #193 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.58.0
2025-04-17 09:08:50 +02:00
Valentin Rothberg b63cf81bb5 vendor: use go 1.23
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-04-17 08:36:09 +02:00
renovate[bot] e86b33e7c5
fix(deps): update module github.com/containers/storage to v1.58.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 16:11:59 +00:00
Giuseppe Scrivano 71fa86684d
Merge pull request #192 from vrothberg/bump
Bump to v1.2.11
2025-03-31 12:59:03 +02:00
Valentin Rothberg 9d8d33bd67 bump to v1.2.12-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-03-31 12:45:30 +02:00
Valentin Rothberg bfdd693beb v1.2.11
Fix an issue where the `read` syscall isn't being correctly recorded on
RHEL 10, see https://issues.redhat.com/browse/RHEL-85351.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-03-31 12:44:05 +02:00
Valentin Rothberg 2fe6ea2d9f
Merge pull request #191 from giuseppe/fix-race-record
ebpf: do not lose syscalls happening before the channel is set
2025-03-31 12:41:52 +02:00
Giuseppe Scrivano 4590fdba26
hook: attach tracepoint after the channel is ready
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-03-31 12:01:26 +02:00
Giuseppe Scrivano a028edf9cb
ebpf: fix the code to do what the comment says
a syscall must be ignored before prctl is seen.

Closes: https://issues.redhat.com/browse/RHEL-85351

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-03-31 12:00:32 +02:00
Valentin Rothberg 22a4f30f48
Merge pull request #187 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.1
2025-03-29 14:59:29 +01:00
renovate[bot] 34b21b6dc3
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-29 13:52:46 +00:00
Valentin Rothberg 63fefe0f20
Merge pull request #188 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.62.3
2025-03-29 14:52:04 +01:00
renovate[bot] 591153cb8f
fix(deps): update module github.com/containers/common to v0.62.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 22:20:14 +00:00
Paul Holzinger 8a24390d18
Merge pull request #190 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20250324
2025-03-24 21:23:58 +01:00
renovate[bot] 97f2983630
chore(deps): update dependency containers/automation_images to v20250324
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 19:40:15 +00:00
Valentin Rothberg e2a50af30e
Merge pull request #185 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.62.0
2025-02-04 11:53:29 +01:00
renovate[bot] 1a717caef7
fix(deps): update module github.com/containers/common to v0.62.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 10:45:59 +00:00
Valentin Rothberg d363e25d2a
Merge pull request #186 from vrothberg/lint
update golangci-lint
2025-02-04 11:45:16 +01:00
Valentin Rothberg bdb2268d7b drop Rawhide
It's constantly breaking due to go version mismatches of Rawhide,
golangci and what not.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2025-02-04 11:36:46 +01:00
Valentin Rothberg c5e4904edc
Merge pull request #183 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20250131
2025-02-04 11:17:58 +01:00
renovate[bot] 630a09f34e
chore(deps): update dependency containers/automation_images to v20250131
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 16:51:14 +00:00
Valentin Rothberg 51eaa377ce
Merge pull request #181 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20241107
2024-11-12 09:02:08 +01:00
Valentin Rothberg b8a02cc18f
Merge pull request #180 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.61.0
2024-11-12 09:01:44 +01:00
renovate[bot] 6c8ecb124e
chore(deps): update dependency containers/automation_images to v20241107
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 16:16:24 +00:00
renovate[bot] 6a2d670f51
fix(deps): update module github.com/containers/common to v0.61.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 19:15:31 +00:00
Valentin Rothberg e9fdef4dbf
Merge pull request #178 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.56.0
2024-11-08 10:48:45 +01:00
renovate[bot] 76a55c7ad9
fix(deps): update module github.com/containers/storage to v1.56.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 09:29:07 +00:00
Valentin Rothberg e6400f5b96
Merge pull request #179 from vrothberg/fix-ci
vendor task: use Go 1.22
2024-11-08 10:28:14 +01:00
Valentin Rothberg bad4003415 vendor task: use Go 1.22
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-11-08 09:07:50 +01:00
Valentin Rothberg a26fed5314
Merge pull request #176 from containers/renovate/go-github.com-containers-common-vulnerability
fix(deps): update module github.com/containers/common to v0.60.4 [security]
2024-11-08 09:02:41 +01:00
renovate[bot] 1a9b6396ef
fix(deps): update module github.com/containers/common to v0.60.4 [security]
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-11 10:47:00 +00:00
Valentin Rothberg b03cedfb33
Merge pull request #177 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20241010
2024-10-11 10:40:13 +02:00
renovate[bot] 1326c0606a
chore(deps): update dependency containers/automation_images to v20241010
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-10 22:05:14 +00:00
Valentin Rothberg 898f7b190e
Merge pull request #173 from vrothberg/bump
bump golangci-lint to v1.60.3
2024-08-29 10:59:38 +02:00
Valentin Rothberg 3b4191616c bump golangci-lint to v1.60.3
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-08-29 10:43:43 +02:00
Valentin Rothberg ff7d010baf
Merge pull request #172 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20240821
2024-08-29 10:40:27 +02:00
renovate[bot] 7af78de4f7
chore(deps): update dependency containers/automation_images to v20240821
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-28 20:13:00 +00:00
Valentin Rothberg 00e2d7245d
Merge pull request #171 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.60.2
2024-08-21 09:22:54 +02:00
renovate[bot] f72ed843c0
fix(deps): update module github.com/containers/common to v0.60.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-20 22:08:52 +00:00
Valentin Rothberg 0bc7da7ab1
Merge pull request #169 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.59.2
2024-07-11 09:56:06 +02:00
renovate[bot] 51015d1966
fix(deps): update module github.com/containers/common to v0.59.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 02:05:32 +00:00
Valentin Rothberg f9b9e536f8
Merge pull request #168 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20240529
2024-05-30 10:02:08 +02:00
renovate[bot] 2f5237395e
chore(deps): update dependency containers/automation_images to v20240529
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-29 20:11:26 +00:00
Valentin Rothberg b897a7d464
Merge pull request #165 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.59.0
2024-05-27 08:44:52 +02:00
Valentin Rothberg 7a6c9d3a3d cirrus vendor: use golang 1.21 image
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-05-27 08:28:38 +02:00
Valentin Rothberg 86b2c1c2cc
Merge pull request #166 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20240513
2024-05-27 08:28:06 +02:00
renovate[bot] 266bdfe05f
fix(deps): update module github.com/containers/common to v0.59.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-23 15:07:43 +00:00
renovate[bot] 1ec8ce61a5
chore(deps): update dependency containers/automation_images to v20240513
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-22 21:35:27 +00:00
Daniel J Walsh ba91441159
Merge pull request #167 from cavokz/allow-overriding-the-build-target-dir
Allow overriding the build target dir
2024-05-22 17:34:27 -04:00
Domenico Andreoli 5553f90b1c
Allow overriding the build target dir
In Debian we build the binary in a different location but still we use
`make install` to populate the package content.

Signed-off-by: Domenico Andreoli <domenico.andreoli@linux.com>
2024-05-22 15:34:48 +02:00
Valentin Rothberg 681179f1bd
Merge pull request #163 from containers/renovate/github.com-stretchr-testify-1.x
fix(deps): update module github.com/stretchr/testify to v1.9.0
2024-03-04 09:06:01 +01:00
renovate[bot] b486e8181e
fix(deps): update module github.com/stretchr/testify to v1.9.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-02 14:01:54 +00:00
Valentin Rothberg cd907412d5
Merge pull request #158 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.52.0
2024-02-16 12:56:33 +01:00
renovate[bot] 969537eb7a
fix(deps): update module github.com/containers/storage to v1.52.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-16 09:11:29 +00:00
Valentin Rothberg ec31ff1e86
Merge pull request #162 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
2024-02-16 10:10:17 +01:00
renovate[bot] fe7a74ceed
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 16:32:20 +00:00
Valentin Rothberg 01ca720a78
Merge pull request #161 from containers/renovate/go-github.com/opencontainers/runc-vulnerability
chore(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
2024-02-05 12:04:14 +01:00
Valentin Rothberg 57e5d60a3f
Merge pull request #156 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.57.4
2024-02-05 12:03:54 +01:00
renovate[bot] 062cbb9697
fix(deps): update module github.com/containers/common to v0.57.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 10:52:12 +00:00
renovate[bot] 12a6b5a7ef
chore(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 10:51:27 +00:00
Valentin Rothberg d7918b036c
Merge pull request #160 from vrothberg/update-ci-image
update CI images
2024-02-05 11:51:00 +01:00
Valentin Rothberg 8b59acfd3d bump golangci-lint to 1.55.2
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-02-05 10:33:54 +01:00
Valentin Rothberg 3082eed8f1
Merge pull request #155 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.57.0
2023-11-17 10:17:21 +01:00
Valentin Rothberg a4c0f838cd
Merge pull request #152 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20231116
2023-11-17 10:16:48 +01:00
renovate[bot] fff84e808d
fix(deps): update module github.com/containers/common to v0.57.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-17 00:05:25 +00:00
renovate[bot] 5ed5002d6c
chore(deps): update dependency containers/automation_images to v20231116
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 21:48:52 +00:00
Valentin Rothberg ad9b137be0
Merge pull request #153 from vrothberg/release
bump to v1.2.10
2023-10-20 10:49:42 +02:00
Valentin Rothberg 1c78fe1b15 bump to v1.2.11-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-10-20 10:36:15 +02:00
446 changed files with 16438 additions and 7606 deletions

View File

@ -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:

View File

@ -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

View File

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

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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

View File

@ -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{

View File

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

View File

@ -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

View File

@ -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": {}
}
]
}

View File

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

View File

@ -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
}

View File

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

View File

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

View 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
}

View File

@ -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
}

View 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
}

View File

@ -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
}

View 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
}

View 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
}

View 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
}

View File

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

View 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
}

View 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
}

View File

@ -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()

View File

@ -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))

View File

@ -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)
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
}
}

View File

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

View File

@ -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)
}
}

View File

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

View File

@ -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)
}

View File

@ -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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

View File

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

View 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
}

View 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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@ func EnsureRemoveAll(dir string) error {
// track retries
exitOnErr := make(map[string]int)
maxRetry := 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)
}
}

View File

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

View File

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

View File

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

View File

@ -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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

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

View File

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

View File

@ -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>

View File

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

View File

@ -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+"=")

View File

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

View File

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

View File

@ -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

View File

@ -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+"=")

View File

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

124
vendor/github.com/moby/sys/capability/CHANGELOG.md generated vendored Normal file
View File

@ -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

View File

@ -1,3 +1,4 @@
Copyright 2023 The Capability Authors.
Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
All rights reserved.

13
vendor/github.com/moby/sys/capability/README.md generated vendored Normal file
View File

@ -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).
[![Go Reference](https://pkg.go.dev/badge/github.com/moby/sys/capability/capability.svg)](https://pkg.go.dev/github.com/moby/sys/capability)
## Alternatives
* https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/cap
[capabilities(7)]: https://man7.org/linux/man-pages/man7/capabilities.7.html

176
vendor/github.com/moby/sys/capability/capability.go generated vendored Normal file
View File

@ -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...)
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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
}

View File

@ -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,

View File

@ -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
}

View File

@ -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

View File

@ -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) {

View File

@ -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.

141
vendor/github.com/moby/sys/user/idtools.go generated vendored Normal file
View File

@ -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