Compare commits
195 Commits
Author | SHA1 | Date |
---|---|---|
|
7c6167725b | |
|
b63cf81bb5 | |
|
e86b33e7c5 | |
|
71fa86684d | |
|
9d8d33bd67 | |
|
bfdd693beb | |
|
2fe6ea2d9f | |
|
4590fdba26 | |
|
a028edf9cb | |
|
22a4f30f48 | |
|
34b21b6dc3 | |
|
63fefe0f20 | |
|
591153cb8f | |
|
8a24390d18 | |
|
97f2983630 | |
|
e2a50af30e | |
|
1a717caef7 | |
|
d363e25d2a | |
|
bdb2268d7b | |
|
c5e4904edc | |
|
630a09f34e | |
|
51eaa377ce | |
|
b8a02cc18f | |
|
6c8ecb124e | |
|
6a2d670f51 | |
|
e9fdef4dbf | |
|
76a55c7ad9 | |
|
e6400f5b96 | |
|
bad4003415 | |
|
a26fed5314 | |
|
1a9b6396ef | |
|
b03cedfb33 | |
|
1326c0606a | |
|
898f7b190e | |
|
3b4191616c | |
|
ff7d010baf | |
|
7af78de4f7 | |
|
00e2d7245d | |
|
f72ed843c0 | |
|
0bc7da7ab1 | |
|
51015d1966 | |
|
f9b9e536f8 | |
|
2f5237395e | |
|
b897a7d464 | |
|
7a6c9d3a3d | |
|
86b2c1c2cc | |
|
266bdfe05f | |
|
1ec8ce61a5 | |
|
ba91441159 | |
|
5553f90b1c | |
|
681179f1bd | |
|
b486e8181e | |
|
cd907412d5 | |
|
969537eb7a | |
|
ec31ff1e86 | |
|
fe7a74ceed | |
|
01ca720a78 | |
|
57e5d60a3f | |
|
062cbb9697 | |
|
12a6b5a7ef | |
|
d7918b036c | |
|
8b59acfd3d | |
|
3082eed8f1 | |
|
a4c0f838cd | |
|
fff84e808d | |
|
5ed5002d6c | |
|
ad9b137be0 | |
|
1c78fe1b15 | |
|
a7bafcb864 | |
|
8cdee7b11e | |
|
e8f359faac | |
|
c6dd74b3e7 | |
|
86c18727bb | |
|
162cf0d113 | |
|
c23a2764ac | |
|
3cbd5e812a | |
|
e8062f8abe | |
|
a12ae46058 | |
|
a868c45bf8 | |
|
32c22ee33f | |
|
560bae97b8 | |
|
8c709c228c | |
|
d560060775 | |
|
21d936bbef | |
|
fb98b83d20 | |
|
61c7d317ea | |
|
55a5929566 | |
|
e98e224fab | |
|
56d0907d7d | |
|
9e495e0a80 | |
|
9745218df4 | |
|
16395c5ed7 | |
|
8a9af6861b | |
|
8c5ed87d79 | |
|
5d4d04f251 | |
|
ce7d972ba4 | |
|
8c4b4dcc6b | |
|
32e2606d27 | |
|
38f6ab6090 | |
|
5e3793b640 | |
|
57823f9c42 | |
|
4cc0634fc1 | |
|
1c8eaf3085 | |
|
d525ee9695 | |
|
37fbd3020f | |
|
cf1b3ce101 | |
|
3b5d827ce8 | |
|
967fea3e8e | |
|
39ea9e139f | |
|
6fdae39069 | |
|
28d462ed74 | |
|
4e983ea172 | |
|
89503c8f6b | |
|
ab5d270372 | |
|
c5eecd3be7 | |
|
d131bebcca | |
|
c3a431d834 | |
|
3718eaafa4 | |
|
b09d71ad89 | |
|
9ca3f9f7b5 | |
|
3280db1a60 | |
|
f1cc6af344 | |
|
00e4b496e1 | |
|
28f1b41688 | |
|
82113f6b3a | |
|
b9068bfb01 | |
|
3f6c6065a8 | |
|
7021239842 | |
|
bed5713b5a | |
|
19d707a515 | |
|
9ee6f0a8ba | |
|
bc40b01a56 | |
|
8cc1f7aedd | |
|
bd33a9cdce | |
|
e6accd4cfe | |
|
544c991c77 | |
|
1003459cbc | |
|
36a96f5d5a | |
|
8013aee634 | |
|
a742ba49c8 | |
|
cc4650c778 | |
|
872fc48dce | |
|
c9476b811f | |
|
d6a8b8c7bf | |
|
b7030ae105 | |
|
2258f0a2ce | |
|
ee91033618 | |
|
2543f4f6e5 | |
|
245cc45ad4 | |
|
934aee1fc5 | |
|
3fee319eaf | |
|
e33c59f550 | |
|
ad1d706bc1 | |
|
0dd7377070 | |
|
1ac29b4441 | |
|
4de230431d | |
|
e3e0f2acff | |
|
623923ef48 | |
|
bdb5040dc4 | |
|
afc6afabe4 | |
|
c57f0ab6d2 | |
|
5c855cb55a | |
|
1244014eee | |
|
ef71fe9e52 | |
|
998f008301 | |
|
5d1ff36303 | |
|
7327b3c07e | |
|
a5abc427ed | |
|
7513108440 | |
|
7f40ff45a2 | |
|
81d698b67b | |
|
8b16fc7912 | |
|
f49c1ba592 | |
|
aac3eb4fa5 | |
|
c378484c29 | |
|
65ccce260b | |
|
6385f4e518 | |
|
641cabfc84 | |
|
7a25813f29 | |
|
bd2d0c048e | |
|
9fac5591f3 | |
|
ca92d4d70e | |
|
4f6665442d | |
|
76f6883c86 | |
|
4a30d950c6 | |
|
e568b43963 | |
|
511749e593 | |
|
70c739725d | |
|
1910bb0fdb | |
|
70364b44d1 | |
|
b8769db559 | |
|
feff03e6dc | |
|
f5cea64ac7 | |
|
b059fab415 | |
|
a076a6ff65 |
69
.cirrus.yml
69
.cirrus.yml
|
@ -11,8 +11,8 @@ env:
|
||||||
GOCACHE: "${HOME}/.cache/go-build"
|
GOCACHE: "${HOME}/.cache/go-build"
|
||||||
|
|
||||||
# VM Image built in containers/automation_images
|
# VM Image built in containers/automation_images
|
||||||
_BUILT_IMAGE_SUFFIX: "c5032481331085312"
|
IMAGE_SUFFIX: "c20250324t111922z-f41f40d13"
|
||||||
FEDORA_CACHE_IMAGE_NAME: "fedora-${_BUILT_IMAGE_SUFFIX}"
|
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
|
||||||
|
|
||||||
# Must be defined true when testing w/in containers
|
# Must be defined true when testing w/in containers
|
||||||
CONTAINER: "false"
|
CONTAINER: "false"
|
||||||
|
@ -21,21 +21,11 @@ env:
|
||||||
gcp_credentials: ENCRYPTED[2ba9cffb563741f8538eab6d4a8b2d4684c0de23693a8ade80aced34596669a87a6c01e45ce45fad7f7db1d995e8c777]
|
gcp_credentials: ENCRYPTED[2ba9cffb563741f8538eab6d4a8b2d4684c0de23693a8ade80aced34596669a87a6c01e45ce45fad7f7db1d995e8c777]
|
||||||
|
|
||||||
|
|
||||||
# Default VM to use unless set or modified by task
|
|
||||||
gce_instance:
|
|
||||||
image_project: "${IMAGE_PROJECT}"
|
|
||||||
zone: "us-central1-c" # Required by Cirrus for the time being
|
|
||||||
cpu: 2
|
|
||||||
memory: "4Gb"
|
|
||||||
disk: 200 # Required for performance reasons
|
|
||||||
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
|
|
||||||
|
|
||||||
|
|
||||||
# Update metadata on VM images referenced by this repository state
|
# Update metadata on VM images referenced by this repository state
|
||||||
meta_task:
|
meta_task:
|
||||||
|
|
||||||
container:
|
container:
|
||||||
image: "quay.io/libpod/imgts:${_BUILT_IMAGE_SUFFIX}"
|
image: "quay.io/libpod/imgts:latest"
|
||||||
cpu: 1
|
cpu: 1
|
||||||
memory: 1
|
memory: 1
|
||||||
|
|
||||||
|
@ -58,47 +48,27 @@ meta_task:
|
||||||
vendor_task:
|
vendor_task:
|
||||||
|
|
||||||
container:
|
container:
|
||||||
image: golang:1.14
|
image: golang:1.23
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- make vendor
|
- make vendor
|
||||||
|
|
||||||
|
|
||||||
# build binary and docs
|
|
||||||
build_task:
|
|
||||||
|
|
||||||
# Uses VM via default gce_instance (above)
|
|
||||||
gce_instance:
|
|
||||||
image_name: $FEDORA_CACHE_IMAGE_NAME
|
|
||||||
|
|
||||||
# Avoid downloading this stuff every time
|
|
||||||
gocache_cache:
|
|
||||||
folder: "${GOCACHE}"
|
|
||||||
fingerprint_script: $SCRIPT_BASE/cache_fingerprint.sh
|
|
||||||
|
|
||||||
# Avoid needless rebuilding of tooling binaries
|
|
||||||
gopath_cache:
|
|
||||||
folder: "${GOPATH}/bin"
|
|
||||||
fingerprint_script: $SCRIPT_BASE/cache_fingerprint.sh
|
|
||||||
|
|
||||||
# Avoid needless rebuilding of source binaries
|
|
||||||
gosrc_bin_cache:
|
|
||||||
folder: "${CIRRUS_WORKING_DIR}/bin"
|
|
||||||
fingerprint_script: $SCRIPT_BASE/cache_fingerprint.sh
|
|
||||||
|
|
||||||
script:
|
|
||||||
- $SCRIPT_BASE/setup.sh
|
|
||||||
- make binary
|
|
||||||
- make install.tools
|
|
||||||
- make validate
|
|
||||||
- make docs
|
|
||||||
|
|
||||||
|
|
||||||
# run unit and integration tests
|
# run unit and integration tests
|
||||||
test_task:
|
build_and_test_task:
|
||||||
|
|
||||||
gce_instance:
|
gce_instance:
|
||||||
image_name: $FEDORA_CACHE_IMAGE_NAME
|
image_project: "${IMAGE_PROJECT}"
|
||||||
|
zone: "us-central1-c" # Required by Cirrus for the time being
|
||||||
|
cpu: 2
|
||||||
|
memory: "4Gb"
|
||||||
|
disk: 200 # Required for performance reasons
|
||||||
|
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
- name: "Test on Fedora"
|
||||||
|
gce_instance:
|
||||||
|
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
|
||||||
|
|
||||||
# Avoid downloading this stuff every time
|
# Avoid downloading this stuff every time
|
||||||
gocache_cache:
|
gocache_cache:
|
||||||
|
@ -123,10 +93,17 @@ test_task:
|
||||||
script:
|
script:
|
||||||
- $SCRIPT_BASE/setup.sh
|
- $SCRIPT_BASE/setup.sh
|
||||||
- make binary
|
- make binary
|
||||||
|
- make install.tools
|
||||||
|
- make validate
|
||||||
|
- make docs
|
||||||
- sudo make PREFIX=/usr install
|
- sudo make PREFIX=/usr install
|
||||||
- make test-unit
|
- make test-unit
|
||||||
- sudo make test-integration
|
- sudo make test-integration
|
||||||
|
|
||||||
|
# The hook runs in the background, errors will be in the journal.
|
||||||
|
always:
|
||||||
|
journal_script: journalctl -b
|
||||||
|
|
||||||
binaries_artifacts:
|
binaries_artifacts:
|
||||||
path: "bin/*"
|
path: "bin/*"
|
||||||
type: "application/octet-stream"
|
type: "application/octet-stream"
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Renovate is a service similar to GitHub Dependabot, but with
|
||||||
|
(fantastically) more configuration options. So many options
|
||||||
|
in fact, if you're new I recommend glossing over this cheat-sheet
|
||||||
|
prior to the official documentation:
|
||||||
|
|
||||||
|
https://www.augmentedmind.de/2021/07/25/renovate-bot-cheat-sheet
|
||||||
|
|
||||||
|
Configuration Update/Change Procedure:
|
||||||
|
1. Make changes
|
||||||
|
2. Manually validate changes (from repo-root):
|
||||||
|
|
||||||
|
podman run -it \
|
||||||
|
-v ./.github/renovate.json5:/usr/src/app/renovate.json5:z \
|
||||||
|
docker.io/renovate/renovate:latest \
|
||||||
|
renovate-config-validator
|
||||||
|
3. Commit.
|
||||||
|
|
||||||
|
Configuration Reference:
|
||||||
|
https://docs.renovatebot.com/configuration-options/
|
||||||
|
|
||||||
|
Monitoring Dashboard:
|
||||||
|
https://app.renovatebot.com/dashboard#github/containers
|
||||||
|
|
||||||
|
Note: The Renovate bot will create/manage it's business on
|
||||||
|
branches named 'renovate/*'. Otherwise, and by
|
||||||
|
default, the only the copy of this file that matters
|
||||||
|
is the one on the `main` branch. No other branches
|
||||||
|
will be monitored or touched in any way.
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
****** Global/general configuration options *****
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
// Re-use predefined sets of configuration options to DRY
|
||||||
|
"extends": [
|
||||||
|
// https://github.com/containers/automation/blob/main/renovate/defaults.json5
|
||||||
|
"github>containers/automation//renovate/defaults.json5"
|
||||||
|
],
|
||||||
|
|
||||||
|
// Permit automatic rebasing when base-branch changes by more than
|
||||||
|
// one commit.
|
||||||
|
"rebaseWhen": "behind-base-branch",
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
*** Repository-specific configuration options ***
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
"assignees": ["vrothberg", "rhatdan"],
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
## The oci-seccomp-bpf-hook Project Community Code of Conduct
|
## The oci-seccomp-bpf-hook Project Community Code of Conduct
|
||||||
|
|
||||||
The oci-seccomp-bpf-hook project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md).
|
The oci-seccomp-bpf-hook project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/main/CODE-OF-CONDUCT.md).
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -6,6 +6,7 @@ GO_BUILD=$(GO) build
|
||||||
ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true)
|
ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true)
|
||||||
GO_BUILD=GO111MODULE=on $(GO) build -mod=vendor
|
GO_BUILD=GO111MODULE=on $(GO) build -mod=vendor
|
||||||
endif
|
endif
|
||||||
|
BUILDDIR ?= .
|
||||||
DESTDIR ?=
|
DESTDIR ?=
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
|
SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z)
|
||||||
|
@ -46,11 +47,11 @@ docs:
|
||||||
|
|
||||||
.PHONY: binary
|
.PHONY: binary
|
||||||
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
|
.PHONY: validate
|
||||||
validate:
|
validate:
|
||||||
$(GOBIN)/golangci-lint run
|
./build/golangci-lint run
|
||||||
|
|
||||||
.PHONY: vendor
|
.PHONY: vendor
|
||||||
vendor:
|
vendor:
|
||||||
|
@ -73,10 +74,9 @@ test-unit:
|
||||||
.PHONY: install.tools
|
.PHONY: install.tools
|
||||||
install.tools: .install.golangci-lint .install.md2man
|
install.tools: .install.golangci-lint .install.md2man
|
||||||
|
|
||||||
|
.install.golangci-lint: VERSION=v1.60.3
|
||||||
.install.golangci-lint:
|
.install.golangci-lint:
|
||||||
if [ ! -x "$(GOBIN)/golangci-lint" ]; then \
|
curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/$(VERSION)/install.sh | sh -s -- -b ./build $(VERSION)
|
||||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOBIN)/ v1.18.0; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
.install.md2man:
|
.install.md2man:
|
||||||
if [ -z "$(shell type -P go-md2man)" ]; then \
|
if [ -z "$(shell type -P go-md2man)" ]; then \
|
||||||
|
@ -95,7 +95,7 @@ install.docs:
|
||||||
install-nobuild: install.docs-nobuild
|
install-nobuild: install.docs-nobuild
|
||||||
install $(SELINUXOPT) -d -m 755 $(DESTDIR)$(HOOK_BIN_DIR)
|
install $(SELINUXOPT) -d -m 755 $(DESTDIR)$(HOOK_BIN_DIR)
|
||||||
install $(SELINUXOPT) -d -m 755 $(DESTDIR)$(HOOK_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)
|
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
|
sed -i 's|HOOK_BIN_DIR|$(HOOK_BIN_DIR)|g' $(DESTDIR)$(HOOK_DIR)/oci-seccomp-bpf-hook.json
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[](https://cirrus-ci.com/github/containers/oci-seccomp-bpf-hook/master)
|
[](https://cirrus-ci.com/github/containers/oci-seccomp-bpf-hook/main)
|
||||||
|
|
||||||
# oci-seccomp-bpf-hook
|
# oci-seccomp-bpf-hook
|
||||||
|
|
||||||
|
@ -23,3 +23,5 @@ sudo podman run --annotation io.containers.trace-syscall="if:[absolute path to t
|
||||||
The profile will be created at the output path provided to the annotation. Providing `of:` is mandatory, while `if:` is optional. An input file can be used to create a baseline and newly recorded syscalls will be added to the set and written to the output. If a syscall is blocked in the base profile, then it will remain blocked in the output file even if it is recorded while tracing.
|
The profile will be created at the output path provided to the annotation. Providing `of:` is mandatory, while `if:` is optional. An input file can be used to create a baseline and newly recorded syscalls will be added to the set and written to the output. If a syscall is blocked in the base profile, then it will remain blocked in the output file even if it is recorded while tracing.
|
||||||
|
|
||||||
Please refer to an article on [Enable Sysadmin](https://www.redhat.com/sysadmin/container-security-seccomp) for more details.
|
Please refer to an article on [Enable Sysadmin](https://www.redhat.com/sysadmin/container-security-seccomp) for more details.
|
||||||
|
|
||||||
|
`Copyright {2018-2022} {containers/oci-seccomp-bpf-hook maintainers}`
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
## Security and Disclosure Information Policy for the oci-seccomp-bpf-hook Project
|
## Security and Disclosure Information Policy for the oci-seccomp-bpf-hook Project
|
||||||
|
|
||||||
The oci-seccomp-bpf-hook Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/master/SECURITY.md) for the Containers Projects.
|
The oci-seccomp-bpf-hook Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/main/SECURITY.md) for the Containers Projects.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM fedora:31
|
FROM fedora:latest
|
||||||
|
|
||||||
ENV GOPATH=/var/tmp/go
|
ENV GOPATH=/var/tmp/go
|
||||||
ENV GOSRC=$GOPATH/src/github.com/containers/oci-seccomp-bpf-hook
|
ENV GOSRC=$GOPATH/src/github.com/containers/oci-seccomp-bpf-hook
|
||||||
|
|
|
@ -10,26 +10,17 @@ set -a
|
||||||
# handling of the (otherwise) default shell setup is non-uniform. Rather
|
# handling of the (otherwise) default shell setup is non-uniform. Rather
|
||||||
# than attempt to workaround differences, simply force-load/set required
|
# than attempt to workaround differences, simply force-load/set required
|
||||||
# items every time this library is utilized.
|
# items every time this library is utilized.
|
||||||
_waserrexit=0
|
|
||||||
if [[ "$SHELLOPTS" =~ errexit ]]; then _waserrexit=1; fi
|
|
||||||
set +e # Assumed in F33 for setting global vars
|
|
||||||
if [[ -r "/etc/automation_environment" ]]; then
|
|
||||||
source /etc/automation_environment
|
|
||||||
else # prior to automation library v2.0, this was necessary
|
|
||||||
source /etc/profile
|
|
||||||
source /etc/environment
|
|
||||||
fi
|
|
||||||
USER="$(whoami)"
|
USER="$(whoami)"
|
||||||
HOME="$(getent passwd $USER | cut -d : -f 6)"
|
HOME="$(getent passwd $USER | cut -d : -f 6)"
|
||||||
# Some platforms set and make this read-only
|
# Some platforms set and make this read-only
|
||||||
[[ -n "$UID" ]] || \
|
[[ -n "$UID" ]] || \
|
||||||
UID=$(getent passwd $USER | cut -d : -f 3)
|
UID=$(getent passwd $USER | cut -d : -f 3)
|
||||||
if ((_waserrexit)); then set -e; fi
|
|
||||||
|
|
||||||
# During VM Image build, the 'containers/automation' installation
|
# Automation library installed at image-build time,
|
||||||
# was performed. The final step of installation sets the library
|
# defining $AUTOMATION_LIB_PATH in this file.
|
||||||
# location $AUTOMATION_LIB_PATH in /etc/environment or in the
|
if [[ -r "/etc/automation_environment" ]]; then
|
||||||
# default shell profile depending on distribution.
|
source /etc/automation_environment
|
||||||
|
fi
|
||||||
# shellcheck disable=SC2154
|
# shellcheck disable=SC2154
|
||||||
if [[ -n "$AUTOMATION_LIB_PATH" ]]; then
|
if [[ -n "$AUTOMATION_LIB_PATH" ]]; then
|
||||||
# shellcheck source=/usr/share/automation/lib/common_lib.sh
|
# shellcheck source=/usr/share/automation/lib/common_lib.sh
|
||||||
|
@ -45,12 +36,6 @@ fi
|
||||||
GOSRC="${GOSRC:-$(realpath $(dirname $0)/../../)}"
|
GOSRC="${GOSRC:-$(realpath $(dirname $0)/../../)}"
|
||||||
SCRIPT_BASE="${SCRIPT_BASE:-./contrib/cirrus}"
|
SCRIPT_BASE="${SCRIPT_BASE:-./contrib/cirrus}"
|
||||||
|
|
||||||
# GCE image-name compatible string representation of distribution name
|
|
||||||
OS_RELEASE_ID="$(source /etc/os-release; echo $ID)"
|
|
||||||
# GCE image-name compatible string representation of distribution _major_ version
|
|
||||||
OS_RELEASE_VER="$(source /etc/os-release; echo $VERSION_ID | cut -d '.' -f 1)"
|
|
||||||
# Combined to ease soe usage
|
|
||||||
OS_REL_VER="${OS_RELEASE_ID}-${OS_RELEASE_VER}"
|
|
||||||
# Set 'true' when operating inside a container
|
# Set 'true' when operating inside a container
|
||||||
CONTAINER="${CONTAINER:-false}"
|
CONTAINER="${CONTAINER:-false}"
|
||||||
|
|
||||||
|
|
9
ebpf.go
9
ebpf.go
|
@ -18,7 +18,6 @@ type event struct {
|
||||||
// Complete documentation is available at
|
// Complete documentation is available at
|
||||||
// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
|
// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
|
||||||
const source string = `
|
const source string = `
|
||||||
#include <linux/bpf.h>
|
|
||||||
#include <linux/nsproxy.h>
|
#include <linux/nsproxy.h>
|
||||||
#include <linux/pid_namespace.h>
|
#include <linux/pid_namespace.h>
|
||||||
#include <linux/ns_common.h>
|
#include <linux/ns_common.h>
|
||||||
|
@ -91,7 +90,6 @@ int enter_trace(struct tracepoint__raw_syscalls__sys_enter* args)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 zero_64 = 0;
|
|
||||||
u64 seen = 0, *tmp = seen_syscalls.lookup(&id);
|
u64 seen = 0, *tmp = seen_syscalls.lookup(&id);
|
||||||
if (tmp != NULL)
|
if (tmp != NULL)
|
||||||
seen = *tmp;
|
seen = *tmp;
|
||||||
|
@ -113,6 +111,13 @@ int enter_trace(struct tracepoint__raw_syscalls__sys_enter* args)
|
||||||
// The syscall was already notified.
|
// The syscall was already notified.
|
||||||
if (seen > 0)
|
if (seen > 0)
|
||||||
return 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;
|
data.stopTracing = false;
|
||||||
|
|
32
go.mod
32
go.mod
|
@ -1,14 +1,28 @@
|
||||||
module github.com/containers/oci-seccomp-bpf-hook
|
module github.com/containers/oci-seccomp-bpf-hook
|
||||||
|
|
||||||
go 1.14
|
go 1.23.0
|
||||||
|
|
||||||
|
toolchain go1.23.8
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/iovisor/gobpf v0.0.0-20210109143822-fb892541d416
|
github.com/containers/common v0.62.3
|
||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
|
github.com/containers/storage v1.58.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4
|
||||||
github.com/seccomp/containers-golang v0.6.0
|
github.com/opencontainers/runtime-spec v1.2.1
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1
|
github.com/seccomp/libseccomp-golang v0.10.0
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.10.0
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
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/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
|
||||||
)
|
)
|
||||||
|
|
105
go.sum
105
go.sum
|
@ -1,60 +1,55 @@
|
||||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
github.com/containers/common v0.62.3 h1:aOGryqXfW6aKBbHbqOveH7zB+ihavUN03X/2pUSvWFI=
|
||||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
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.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.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/iovisor/gobpf v0.0.0-20210109143822-fb892541d416 h1:KIP9lHDm4Y+rNr6ONL+5fC5lX+79FgRlF6Xmi9VasVk=
|
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4 h1:WpizD4VUT5V+VcaQSvW5BlvFpQYrd2974H9KbiGa5/0=
|
||||||
github.com/iovisor/gobpf v0.0.0-20210109143822-fb892541d416/go.mod h1:+5U5qu5UOu8YJ5oHVLvWKH7/Dr5QNHU7mZ2RfPEeXg8=
|
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4/go.mod h1:WSY9Jj5RhdgC3ci1QaacvbFdQ8cbrEjrpiZbLHLt2s4=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200710190001-3e4195d92445/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 h1:NhsM2gc769rVWDqJvapK37r+7+CBXI8xHhnfnt8uQsg=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY=
|
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
|
||||||
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
|
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/seccomp/containers-golang v0.6.0 h1:VWPMMIDr8pAtNjCX0WvLEEK9EQi5lAm4HtJbDtAtFvQ=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/seccomp/containers-golang v0.6.0/go.mod h1:Dd9mONHvW4YdbSzdm23yf2CFw0iqvqLhO0mEFvPIvm4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243 h1:R43TdZy32XXSXjJn7M/HhALJ9imq6ztLnChfYJpVDnM=
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8=
|
|
||||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -5,9 +5,9 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -21,10 +21,10 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
types "github.com/containers/common/pkg/seccomp"
|
||||||
|
"github.com/containers/storage/pkg/unshare"
|
||||||
"github.com/iovisor/gobpf/bcc"
|
"github.com/iovisor/gobpf/bcc"
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
|
||||||
types "github.com/seccomp/containers-golang"
|
|
||||||
seccomp "github.com/seccomp/libseccomp-golang"
|
seccomp "github.com/seccomp/libseccomp-golang"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
||||||
|
@ -57,6 +57,11 @@ func main() {
|
||||||
logrus.AddHook(hook)
|
logrus.AddHook(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if os.Getuid() != 0 || unshare.IsRootless() {
|
||||||
|
logrus.Errorf("running the hook requires root privileges")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
runBPF := flag.Int("r", 0, "Trace the specified PID")
|
runBPF := flag.Int("r", 0, "Trace the specified PID")
|
||||||
outputFile := flag.String("o", "", "Path of the output file")
|
outputFile := flag.String("o", "", "Path of the output file")
|
||||||
inputFile := flag.String("i", "", "Path of the input file")
|
inputFile := flag.String("i", "", "Path of the input file")
|
||||||
|
@ -124,7 +129,7 @@ func detachAndTrace() error {
|
||||||
|
|
||||||
// Sanity check the PID.
|
// Sanity check the PID.
|
||||||
if s.Pid <= 0 {
|
if s.Pid <= 0 {
|
||||||
return errors.Errorf("invalid PID %d (must be greater than 0)", s.Pid)
|
return fmt.Errorf("invalid PID %d (must be greater than 0)", s.Pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the State's annotation.
|
// Parse the State's annotation.
|
||||||
|
@ -159,12 +164,12 @@ func detachAndTrace() error {
|
||||||
|
|
||||||
executable, err := os.Executable()
|
executable, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "cannot determine executable")
|
return fmt.Errorf("cannot determine executable: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
process, err := os.StartProcess(executable, []string{"oci-seccomp-bpf-hook", "-r", strconv.Itoa(s.Pid), "-o", outputFile, "-i", inputFile}, attr)
|
process, err := os.StartProcess(executable, []string{"oci-seccomp-bpf-hook", "-r", strconv.Itoa(s.Pid), "-o", outputFile, "-i", inputFile}, attr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "cannot re-execute")
|
return fmt.Errorf("cannot re-execute: %v", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := process.Release(); err != nil {
|
if err := process.Release(); err != nil {
|
||||||
|
@ -183,7 +188,7 @@ func detachAndTrace() error {
|
||||||
case syscall.SIGUSR2:
|
case syscall.SIGUSR2:
|
||||||
return errors.New("error while tracing")
|
return errors.New("error while tracing")
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("unexpected signal %v", s)
|
return fmt.Errorf("unexpected signal %v", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The timeout kicked in. Kill the child and return the sad news.
|
// The timeout kicked in. Kill the child and return the sad news.
|
||||||
|
@ -191,7 +196,7 @@ func detachAndTrace() error {
|
||||||
if err := process.Kill(); err != nil {
|
if err := process.Kill(); err != nil {
|
||||||
logrus.Errorf("error killing child process: %v", err)
|
logrus.Errorf("error killing child process: %v", err)
|
||||||
}
|
}
|
||||||
return errors.Errorf("BPF program didn't compile and attach within %d seconds", BPFTimeout)
|
return fmt.Errorf("BPF program didn't compile and attach within %d seconds", BPFTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -204,7 +209,7 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
|
||||||
ppid := os.Getppid()
|
ppid := os.Getppid()
|
||||||
parentProcess, err := os.FindProcess(ppid)
|
parentProcess, err := os.FindProcess(ppid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "cannot find parent process %d", ppid)
|
return fmt.Errorf("cannot find parent process %d: %v", ppid, err)
|
||||||
}
|
}
|
||||||
logrus.Infof("Running floating process PID to attach: %d", pid)
|
logrus.Infof("Running floating process PID to attach: %d", pid)
|
||||||
|
|
||||||
|
@ -223,30 +228,30 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
|
||||||
m := bcc.NewModule(src, []string{})
|
m := bcc.NewModule(src, []string{})
|
||||||
defer m.Close()
|
defer m.Close()
|
||||||
|
|
||||||
logrus.Info("Loading enter tracepoint")
|
|
||||||
enterTrace, err := m.LoadTracepoint("enter_trace")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error loading tracepoint")
|
|
||||||
}
|
|
||||||
logrus.Info("Loading exit tracepoint")
|
|
||||||
checkExit, err := m.LoadTracepoint("check_exit")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error loading tracepoint")
|
|
||||||
}
|
|
||||||
logrus.Info("Loaded tracepoints")
|
|
||||||
|
|
||||||
if err := m.AttachTracepoint("raw_syscalls:sys_enter", enterTrace); err != nil {
|
|
||||||
return errors.Wrap(err, "error attaching to tracepoint")
|
|
||||||
}
|
|
||||||
if err := m.AttachTracepoint("sched:sched_process_exit", checkExit); err != nil {
|
|
||||||
return errors.Wrap(err, "error attaching to tracepoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
table := bcc.NewTable(m.TableId("events"), m)
|
table := bcc.NewTable(m.TableId("events"), m)
|
||||||
channel := make(chan []byte)
|
channel := make(chan []byte)
|
||||||
perfMap, err := bcc.InitPerfMap(table, channel, nil)
|
perfMap, err := bcc.InitPerfMap(table, channel, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error initializing perf map")
|
return fmt.Errorf("error initializing perf map: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Info("Loading enter tracepoint")
|
||||||
|
enterTrace, err := m.LoadTracepoint("enter_trace")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error loading tracepoint: %v", err)
|
||||||
|
}
|
||||||
|
logrus.Info("Loading exit tracepoint")
|
||||||
|
checkExit, err := m.LoadTracepoint("check_exit")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error loading tracepoint: %v", err)
|
||||||
|
}
|
||||||
|
logrus.Info("Loaded tracepoints")
|
||||||
|
|
||||||
|
if err := m.AttachTracepoint("raw_syscalls:sys_enter", enterTrace); err != nil {
|
||||||
|
return fmt.Errorf("error attaching to tracepoint: %v", err)
|
||||||
|
}
|
||||||
|
if err := m.AttachTracepoint("sched:sched_process_exit", checkExit); err != nil {
|
||||||
|
return fmt.Errorf("error attaching to tracepoint: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the wait group used to wait for the tracing to be finished.
|
// Initialize the wait group used to wait for the tracing to be finished.
|
||||||
|
@ -300,6 +305,7 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
|
||||||
name, err := syscallIDtoName(e.ID)
|
name, err := syscallIDtoName(e.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("error getting the name for syscall ID %d", e.ID)
|
logrus.Errorf("error getting the name for syscall ID %d", e.ID)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
syscalls[name]++
|
syscalls[name]++
|
||||||
}
|
}
|
||||||
|
@ -309,7 +315,7 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
|
||||||
|
|
||||||
logrus.Infof("Writing seccomp profile to %q", profilePath)
|
logrus.Infof("Writing seccomp profile to %q", profilePath)
|
||||||
if err := generateProfile(syscalls, profilePath, inputFile); err != nil {
|
if err := generateProfile(syscalls, profilePath, inputFile); err != nil {
|
||||||
return errors.Wrap(err, "error generating final seccomp profile")
|
return fmt.Errorf("error generating final seccomp profile: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -321,13 +327,13 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
|
||||||
inputProfile := types.Seccomp{}
|
inputProfile := types.Seccomp{}
|
||||||
|
|
||||||
if inputFile != "" {
|
if inputFile != "" {
|
||||||
input, err := ioutil.ReadFile(inputFile)
|
input, err := os.ReadFile(inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error reading input file")
|
return fmt.Errorf("error reading input file: %v", err)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(input, &inputProfile)
|
err = json.Unmarshal(input, &inputProfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error parsing input file")
|
return fmt.Errorf("error parsing input file: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +351,7 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
|
||||||
outputProfile.DefaultAction = types.ActErrno
|
outputProfile.DefaultAction = types.ActErrno
|
||||||
|
|
||||||
if err := appendArchIfNotAlreadyIncluded(runtime.GOARCH, &outputProfile); err != nil {
|
if err := appendArchIfNotAlreadyIncluded(runtime.GOARCH, &outputProfile); err != nil {
|
||||||
return errors.Wrap(err, "appending architecture to output profile")
|
return fmt.Errorf("appending architecture to output profile: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
outputProfile.Syscalls = append(outputProfile.Syscalls, &types.Syscall{
|
outputProfile.Syscalls = append(outputProfile.Syscalls, &types.Syscall{
|
||||||
|
@ -356,10 +362,10 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
|
||||||
|
|
||||||
sJSON, err := json.Marshal(outputProfile)
|
sJSON, err := json.Marshal(outputProfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error writing seccomp profile")
|
return fmt.Errorf("error writing seccomp profile: %v", err)
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(profilePath, sJSON, 0644); err != nil {
|
if err := os.WriteFile(profilePath, sJSON, 0644); err != nil {
|
||||||
return errors.Wrap(err, "error writing seccomp profile")
|
return fmt.Errorf("error writing seccomp profile: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -369,7 +375,7 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
|
||||||
func parseAnnotation(annotation string) (outputFile string, inputFile string, err error) {
|
func parseAnnotation(annotation string) (outputFile string, inputFile string, err error) {
|
||||||
annotationSplit := strings.Split(annotation, ";")
|
annotationSplit := strings.Split(annotation, ";")
|
||||||
if len(annotationSplit) > 2 {
|
if len(annotationSplit) > 2 {
|
||||||
return "", "", errors.Wrapf(errInvalidAnnotation, "more than one semi-colon: %q", annotation)
|
return "", "", fmt.Errorf("%v: more than one semi-colon: %q", errInvalidAnnotation, annotation)
|
||||||
}
|
}
|
||||||
for _, path := range annotationSplit {
|
for _, path := range annotationSplit {
|
||||||
switch {
|
switch {
|
||||||
|
@ -377,33 +383,33 @@ func parseAnnotation(annotation string) (outputFile string, inputFile string, er
|
||||||
case strings.HasPrefix(path, "if:"):
|
case strings.HasPrefix(path, "if:"):
|
||||||
inputFile = strings.TrimSpace(strings.TrimPrefix(path, InputPrefix))
|
inputFile = strings.TrimSpace(strings.TrimPrefix(path, InputPrefix))
|
||||||
if !filepath.IsAbs(inputFile) {
|
if !filepath.IsAbs(inputFile) {
|
||||||
return "", "", errors.Wrapf(errInvalidAnnotation, "input file path must be absolute: %q", inputFile)
|
return "", "", fmt.Errorf("%v: input file path must be absolute: %q", errInvalidAnnotation, inputFile)
|
||||||
}
|
}
|
||||||
inputProfile := types.Seccomp{}
|
inputProfile := types.Seccomp{}
|
||||||
input, err := ioutil.ReadFile(inputFile)
|
input, err := os.ReadFile(inputFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", errors.Wrapf(errInvalidAnnotation, "error reading input file: %q", inputFile)
|
return "", "", fmt.Errorf("%v: error reading input file: %q", errInvalidAnnotation, inputFile)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(input, &inputProfile)
|
err = json.Unmarshal(input, &inputProfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", errors.Wrapf(errInvalidAnnotation, "error parsing input file: %q", inputFile)
|
return "", "", fmt.Errorf("%v: error parsing input file: %q", errInvalidAnnotation, inputFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output profile
|
// Output profile
|
||||||
case strings.HasPrefix(path, "of:"):
|
case strings.HasPrefix(path, "of:"):
|
||||||
outputFile = strings.TrimSpace(strings.TrimPrefix(path, OutputPrefix))
|
outputFile = strings.TrimSpace(strings.TrimPrefix(path, OutputPrefix))
|
||||||
if !filepath.IsAbs(outputFile) {
|
if !filepath.IsAbs(outputFile) {
|
||||||
return "", "", errors.Wrapf(errInvalidAnnotation, "output file path must be absolute: %q", outputFile)
|
return "", "", fmt.Errorf("%v: output file path must be absolute: %q", errInvalidAnnotation, outputFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsupported default
|
// Unsupported default
|
||||||
default:
|
default:
|
||||||
return "", "", errors.Wrapf(errInvalidAnnotation, "must start %q or %q prefix", InputPrefix, OutputPrefix)
|
return "", "", fmt.Errorf("%v: must start %q or %q prefix", errInvalidAnnotation, InputPrefix, OutputPrefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if outputFile == "" {
|
if outputFile == "" {
|
||||||
return "", "", errors.Wrap(errInvalidAnnotation, "providing output file is mandatory")
|
return "", "", fmt.Errorf("%v: providing output file is mandatory", errInvalidAnnotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputFile, inputFile, nil
|
return outputFile, inputFile, nil
|
||||||
|
@ -432,7 +438,7 @@ func syscallInProfile(profile *types.Seccomp, syscall string) bool {
|
||||||
func appendArchIfNotAlreadyIncluded(goArch string, profile *types.Seccomp) error {
|
func appendArchIfNotAlreadyIncluded(goArch string, profile *types.Seccomp) error {
|
||||||
targetArch, err := types.GoArchToSeccompArch(goArch)
|
targetArch, err := types.GoArchToSeccompArch(goArch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "determine target architecture")
|
return fmt.Errorf("determine target architecture: %v", err)
|
||||||
}
|
}
|
||||||
for _, arch := range profile.Architectures {
|
for _, arch := range profile.Architectures {
|
||||||
if arch == targetArch {
|
if arch == targetArch {
|
||||||
|
|
|
@ -2,12 +2,11 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
types "github.com/seccomp/containers-golang"
|
types "github.com/containers/common/pkg/seccomp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +14,7 @@ func TestParseAnnotation(t *testing.T) {
|
||||||
testProfile := types.Seccomp{}
|
testProfile := types.Seccomp{}
|
||||||
testProfile.DefaultAction = types.ActErrno
|
testProfile.DefaultAction = types.ActErrno
|
||||||
|
|
||||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "input-*.json")
|
tmpFile, err := os.CreateTemp(os.TempDir(), "input-*.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("cannot create temporary file")
|
t.Fatalf("cannot create temporary file")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
// NOTE: this package has originally been copied from
|
||||||
|
// github.com/opencontainers/runc and modified to work for other use cases
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
goArchToSeccompArchMap = map[string]Arch{
|
||||||
|
"386": ArchX86,
|
||||||
|
"amd64": ArchX86_64,
|
||||||
|
"amd64p32": ArchX32,
|
||||||
|
"arm": ArchARM,
|
||||||
|
"arm64": ArchAARCH64,
|
||||||
|
"mips": ArchMIPS,
|
||||||
|
"mips64": ArchMIPS64,
|
||||||
|
"mips64le": ArchMIPSEL64,
|
||||||
|
"mips64p32": ArchMIPS64N32,
|
||||||
|
"mips64p32le": ArchMIPSEL64N32,
|
||||||
|
"mipsle": ArchMIPSEL,
|
||||||
|
"ppc": ArchPPC,
|
||||||
|
"ppc64": ArchPPC64,
|
||||||
|
"ppc64le": ArchPPC64LE,
|
||||||
|
"s390": ArchS390,
|
||||||
|
"s390x": ArchS390X,
|
||||||
|
}
|
||||||
|
specArchToLibseccompArchMap = map[specs.Arch]string{
|
||||||
|
specs.ArchX86: "x86",
|
||||||
|
specs.ArchX86_64: "amd64",
|
||||||
|
specs.ArchX32: "x32",
|
||||||
|
specs.ArchARM: "arm",
|
||||||
|
specs.ArchAARCH64: "arm64",
|
||||||
|
specs.ArchMIPS: "mips",
|
||||||
|
specs.ArchMIPS64: "mips64",
|
||||||
|
specs.ArchMIPS64N32: "mips64n32",
|
||||||
|
specs.ArchMIPSEL: "mipsel",
|
||||||
|
specs.ArchMIPSEL64: "mipsel64",
|
||||||
|
specs.ArchMIPSEL64N32: "mipsel64n32",
|
||||||
|
specs.ArchPPC: "ppc",
|
||||||
|
specs.ArchPPC64: "ppc64",
|
||||||
|
specs.ArchPPC64LE: "ppc64le",
|
||||||
|
specs.ArchS390: "s390",
|
||||||
|
specs.ArchS390X: "s390x",
|
||||||
|
}
|
||||||
|
specArchToSeccompArchMap = map[specs.Arch]Arch{
|
||||||
|
specs.ArchX86: ArchX86,
|
||||||
|
specs.ArchX86_64: ArchX86_64,
|
||||||
|
specs.ArchX32: ArchX32,
|
||||||
|
specs.ArchARM: ArchARM,
|
||||||
|
specs.ArchAARCH64: ArchAARCH64,
|
||||||
|
specs.ArchMIPS: ArchMIPS,
|
||||||
|
specs.ArchMIPS64: ArchMIPS64,
|
||||||
|
specs.ArchMIPS64N32: ArchMIPS64N32,
|
||||||
|
specs.ArchMIPSEL: ArchMIPSEL,
|
||||||
|
specs.ArchMIPSEL64: ArchMIPSEL64,
|
||||||
|
specs.ArchMIPSEL64N32: ArchMIPSEL64N32,
|
||||||
|
specs.ArchPPC: ArchPPC,
|
||||||
|
specs.ArchPPC64: ArchPPC64,
|
||||||
|
specs.ArchPPC64LE: ArchPPC64LE,
|
||||||
|
specs.ArchS390: ArchS390,
|
||||||
|
specs.ArchS390X: ArchS390X,
|
||||||
|
}
|
||||||
|
specActionToSeccompActionMap = map[specs.LinuxSeccompAction]Action{
|
||||||
|
specs.ActKill: ActKill,
|
||||||
|
// TODO: wait for this PR to get merged:
|
||||||
|
// https://github.com/opencontainers/runtime-spec/pull/1064
|
||||||
|
// specs.ActKillProcess ActKillProcess,
|
||||||
|
// specs.ActKillThread ActKillThread,
|
||||||
|
specs.ActErrno: ActErrno,
|
||||||
|
specs.ActTrap: ActTrap,
|
||||||
|
specs.ActAllow: ActAllow,
|
||||||
|
specs.ActTrace: ActTrace,
|
||||||
|
specs.ActLog: ActLog,
|
||||||
|
specs.ActNotify: ActNotify,
|
||||||
|
}
|
||||||
|
specOperatorToSeccompOperatorMap = map[specs.LinuxSeccompOperator]Operator{
|
||||||
|
specs.OpNotEqual: OpNotEqual,
|
||||||
|
specs.OpLessThan: OpLessThan,
|
||||||
|
specs.OpLessEqual: OpLessEqual,
|
||||||
|
specs.OpEqualTo: OpEqualTo,
|
||||||
|
specs.OpGreaterEqual: OpGreaterEqual,
|
||||||
|
specs.OpGreaterThan: OpGreaterThan,
|
||||||
|
specs.OpMaskedEqual: OpMaskedEqual,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GoArchToSeccompArch converts a runtime.GOARCH to a seccomp `Arch`. The
|
||||||
|
// function returns an error if the architecture conversion is not supported.
|
||||||
|
func GoArchToSeccompArch(goArch string) (Arch, error) {
|
||||||
|
arch, ok := goArchToSeccompArchMap[goArch]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("unsupported go arch provided: %s", goArch)
|
||||||
|
}
|
||||||
|
return arch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// specToSeccomp converts a `LinuxSeccomp` spec into a `Seccomp` struct.
|
||||||
|
func specToSeccomp(spec *specs.LinuxSeccomp) (*Seccomp, error) {
|
||||||
|
res := &Seccomp{
|
||||||
|
Syscalls: []*Syscall{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arch := range spec.Architectures {
|
||||||
|
newArch, err := specArchToSeccompArch(arch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("convert spec arch: %w", err)
|
||||||
|
}
|
||||||
|
res.Architectures = append(res.Architectures, newArch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert default action
|
||||||
|
newDefaultAction, err := specActionToSeccompAction(spec.DefaultAction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("convert default action: %w", err)
|
||||||
|
}
|
||||||
|
res.DefaultAction = newDefaultAction
|
||||||
|
res.DefaultErrnoRet = spec.DefaultErrnoRet
|
||||||
|
|
||||||
|
// Loop through all syscall blocks and convert them to the internal format
|
||||||
|
for _, call := range spec.Syscalls {
|
||||||
|
newAction, err := specActionToSeccompAction(call.Action)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("convert action: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range call.Names {
|
||||||
|
newCall := Syscall{
|
||||||
|
Name: name,
|
||||||
|
Action: newAction,
|
||||||
|
ErrnoRet: call.ErrnoRet,
|
||||||
|
Args: []*Arg{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all the arguments of the syscall and convert them
|
||||||
|
for _, arg := range call.Args {
|
||||||
|
newOp, err := specOperatorToSeccompOperator(arg.Op)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("convert operator: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newArg := Arg{
|
||||||
|
Index: arg.Index,
|
||||||
|
Value: arg.Value,
|
||||||
|
ValueTwo: arg.ValueTwo,
|
||||||
|
Op: newOp,
|
||||||
|
}
|
||||||
|
|
||||||
|
newCall.Args = append(newCall.Args, &newArg)
|
||||||
|
}
|
||||||
|
res.Syscalls = append(res.Syscalls, &newCall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// specArchToLibseccompArch converts a spec arch into a libseccomp one.
|
||||||
|
func specArchToLibseccompArch(arch specs.Arch) (string, error) {
|
||||||
|
if res, ok := specArchToLibseccompArchMap[arch]; ok {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"architecture %q is not valid for libseccomp", arch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specArchToSeccompArch converts a spec arch into an internal one.
|
||||||
|
func specArchToSeccompArch(arch specs.Arch) (Arch, error) {
|
||||||
|
if res, ok := specArchToSeccompArchMap[arch]; ok {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("architecture %q is not valid", arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specActionToSeccompAction converts a spec action into a seccomp one.
|
||||||
|
func specActionToSeccompAction(action specs.LinuxSeccompAction) (Action, error) {
|
||||||
|
if res, ok := specActionToSeccompActionMap[action]; ok {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"spec action %q is not valid internal action", action,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specOperatorToSeccompOperator converts a spec operator into a seccomp one.
|
||||||
|
func specOperatorToSeccompOperator(operator specs.LinuxSeccompOperator) (Operator, error) {
|
||||||
|
if op, ok := specOperatorToSeccompOperatorMap[operator]; ok {
|
||||||
|
return op, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"spec operator %q is not a valid internal operator", operator,
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,14 +1,10 @@
|
||||||
// +build seccomp
|
|
||||||
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Copyright 2013-2018 Docker, Inc.
|
// Copyright 2013-2018 Docker, Inc.
|
||||||
|
|
||||||
package seccomp // import "github.com/seccomp/containers-golang"
|
package seccomp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,9 +43,54 @@ func arches() []Architecture {
|
||||||
|
|
||||||
// DefaultProfile defines the allowlist for the default seccomp profile.
|
// DefaultProfile defines the allowlist for the default seccomp profile.
|
||||||
func DefaultProfile() *Seccomp {
|
func DefaultProfile() *Seccomp {
|
||||||
einval := uint(syscall.EINVAL)
|
einval := uint(unix.EINVAL)
|
||||||
|
enosys := uint(unix.ENOSYS)
|
||||||
|
eperm := uint(unix.EPERM)
|
||||||
|
|
||||||
syscalls := []*Syscall{
|
syscalls := []*Syscall{
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
"nice",
|
||||||
|
"oldfstat",
|
||||||
|
"oldlstat",
|
||||||
|
"oldolduname",
|
||||||
|
"oldstat",
|
||||||
|
"olduname",
|
||||||
|
"pciconfig_iobase",
|
||||||
|
"pciconfig_read",
|
||||||
|
"pciconfig_write",
|
||||||
|
"sgetmask",
|
||||||
|
"ssetmask",
|
||||||
|
"swapoff",
|
||||||
|
"swapon",
|
||||||
|
"syscall",
|
||||||
|
"sysfs",
|
||||||
|
"uselib",
|
||||||
|
"userfaultfd",
|
||||||
|
"ustat",
|
||||||
|
"vm86",
|
||||||
|
"vm86old",
|
||||||
|
"vmsplice",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"_llseek",
|
"_llseek",
|
||||||
|
@ -67,10 +108,18 @@ func DefaultProfile() *Seccomp {
|
||||||
"chmod",
|
"chmod",
|
||||||
"chown",
|
"chown",
|
||||||
"chown32",
|
"chown32",
|
||||||
|
"clock_adjtime",
|
||||||
|
"clock_adjtime64",
|
||||||
"clock_getres",
|
"clock_getres",
|
||||||
|
"clock_getres_time64",
|
||||||
"clock_gettime",
|
"clock_gettime",
|
||||||
|
"clock_gettime64",
|
||||||
"clock_nanosleep",
|
"clock_nanosleep",
|
||||||
|
"clock_nanosleep_time64",
|
||||||
|
"clone",
|
||||||
|
"clone3",
|
||||||
"close",
|
"close",
|
||||||
|
"close_range",
|
||||||
"connect",
|
"connect",
|
||||||
"copy_file_range",
|
"copy_file_range",
|
||||||
"creat",
|
"creat",
|
||||||
|
@ -82,6 +131,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"epoll_ctl",
|
"epoll_ctl",
|
||||||
"epoll_ctl_old",
|
"epoll_ctl_old",
|
||||||
"epoll_pwait",
|
"epoll_pwait",
|
||||||
|
"epoll_pwait2",
|
||||||
"epoll_wait",
|
"epoll_wait",
|
||||||
"epoll_wait_old",
|
"epoll_wait_old",
|
||||||
"eventfd",
|
"eventfd",
|
||||||
|
@ -91,6 +141,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"exit",
|
"exit",
|
||||||
"exit_group",
|
"exit_group",
|
||||||
"faccessat",
|
"faccessat",
|
||||||
|
"faccessat2",
|
||||||
"fadvise64",
|
"fadvise64",
|
||||||
"fadvise64_64",
|
"fadvise64_64",
|
||||||
"fallocate",
|
"fallocate",
|
||||||
|
@ -98,6 +149,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"fchdir",
|
"fchdir",
|
||||||
"fchmod",
|
"fchmod",
|
||||||
"fchmodat",
|
"fchmodat",
|
||||||
|
"fchmodat2",
|
||||||
"fchown",
|
"fchown",
|
||||||
"fchown32",
|
"fchown32",
|
||||||
"fchownat",
|
"fchownat",
|
||||||
|
@ -109,7 +161,11 @@ func DefaultProfile() *Seccomp {
|
||||||
"flock",
|
"flock",
|
||||||
"fork",
|
"fork",
|
||||||
"fremovexattr",
|
"fremovexattr",
|
||||||
|
"fsconfig",
|
||||||
"fsetxattr",
|
"fsetxattr",
|
||||||
|
"fsmount",
|
||||||
|
"fsopen",
|
||||||
|
"fspick",
|
||||||
"fstat",
|
"fstat",
|
||||||
"fstat64",
|
"fstat64",
|
||||||
"fstatat64",
|
"fstatat64",
|
||||||
|
@ -119,7 +175,9 @@ func DefaultProfile() *Seccomp {
|
||||||
"ftruncate",
|
"ftruncate",
|
||||||
"ftruncate64",
|
"ftruncate64",
|
||||||
"futex",
|
"futex",
|
||||||
|
"futex_time64",
|
||||||
"futimesat",
|
"futimesat",
|
||||||
|
"get_mempolicy",
|
||||||
"get_robust_list",
|
"get_robust_list",
|
||||||
"get_thread_area",
|
"get_thread_area",
|
||||||
"getcpu",
|
"getcpu",
|
||||||
|
@ -169,7 +227,11 @@ func DefaultProfile() *Seccomp {
|
||||||
"ioprio_get",
|
"ioprio_get",
|
||||||
"ioprio_set",
|
"ioprio_set",
|
||||||
"ipc",
|
"ipc",
|
||||||
|
"keyctl",
|
||||||
"kill",
|
"kill",
|
||||||
|
"landlock_add_rule",
|
||||||
|
"landlock_create_ruleset",
|
||||||
|
"landlock_restrict_self",
|
||||||
"lchown",
|
"lchown",
|
||||||
"lchown32",
|
"lchown32",
|
||||||
"lgetxattr",
|
"lgetxattr",
|
||||||
|
@ -184,7 +246,10 @@ func DefaultProfile() *Seccomp {
|
||||||
"lstat",
|
"lstat",
|
||||||
"lstat64",
|
"lstat64",
|
||||||
"madvise",
|
"madvise",
|
||||||
|
"mbind",
|
||||||
|
"membarrier",
|
||||||
"memfd_create",
|
"memfd_create",
|
||||||
|
"memfd_secret",
|
||||||
"mincore",
|
"mincore",
|
||||||
"mkdir",
|
"mkdir",
|
||||||
"mkdirat",
|
"mkdirat",
|
||||||
|
@ -196,12 +261,16 @@ func DefaultProfile() *Seccomp {
|
||||||
"mmap",
|
"mmap",
|
||||||
"mmap2",
|
"mmap2",
|
||||||
"mount",
|
"mount",
|
||||||
|
"mount_setattr",
|
||||||
|
"move_mount",
|
||||||
"mprotect",
|
"mprotect",
|
||||||
"mq_getsetattr",
|
"mq_getsetattr",
|
||||||
"mq_notify",
|
"mq_notify",
|
||||||
"mq_open",
|
"mq_open",
|
||||||
"mq_timedreceive",
|
"mq_timedreceive",
|
||||||
|
"mq_timedreceive_time64",
|
||||||
"mq_timedsend",
|
"mq_timedsend",
|
||||||
|
"mq_timedsend_time64",
|
||||||
"mq_unlink",
|
"mq_unlink",
|
||||||
"mremap",
|
"mremap",
|
||||||
"msgctl",
|
"msgctl",
|
||||||
|
@ -216,18 +285,33 @@ func DefaultProfile() *Seccomp {
|
||||||
"nanosleep",
|
"nanosleep",
|
||||||
"newfstatat",
|
"newfstatat",
|
||||||
"open",
|
"open",
|
||||||
|
"open_tree",
|
||||||
"openat",
|
"openat",
|
||||||
|
"openat2",
|
||||||
"pause",
|
"pause",
|
||||||
|
"pidfd_getfd",
|
||||||
|
"pidfd_open",
|
||||||
|
"pidfd_send_signal",
|
||||||
"pipe",
|
"pipe",
|
||||||
"pipe2",
|
"pipe2",
|
||||||
|
"pivot_root",
|
||||||
|
"pkey_alloc",
|
||||||
|
"pkey_free",
|
||||||
|
"pkey_mprotect",
|
||||||
"poll",
|
"poll",
|
||||||
"ppoll",
|
"ppoll",
|
||||||
|
"ppoll_time64",
|
||||||
"prctl",
|
"prctl",
|
||||||
"pread64",
|
"pread64",
|
||||||
"preadv",
|
"preadv",
|
||||||
"preadv2",
|
"preadv2",
|
||||||
"prlimit64",
|
"prlimit64",
|
||||||
|
"process_mrelease",
|
||||||
|
"process_vm_readv",
|
||||||
|
"process_vm_writev",
|
||||||
"pselect6",
|
"pselect6",
|
||||||
|
"pselect6_time64",
|
||||||
|
"ptrace",
|
||||||
"pwrite64",
|
"pwrite64",
|
||||||
"pwritev",
|
"pwritev",
|
||||||
"pwritev2",
|
"pwritev2",
|
||||||
|
@ -240,6 +324,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"recv",
|
"recv",
|
||||||
"recvfrom",
|
"recvfrom",
|
||||||
"recvmmsg",
|
"recvmmsg",
|
||||||
|
"recvmmsg_time64",
|
||||||
"recvmsg",
|
"recvmsg",
|
||||||
"remap_file_pages",
|
"remap_file_pages",
|
||||||
"removexattr",
|
"removexattr",
|
||||||
|
@ -248,6 +333,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"renameat2",
|
"renameat2",
|
||||||
"restart_syscall",
|
"restart_syscall",
|
||||||
"rmdir",
|
"rmdir",
|
||||||
|
"rseq",
|
||||||
"rt_sigaction",
|
"rt_sigaction",
|
||||||
"rt_sigpending",
|
"rt_sigpending",
|
||||||
"rt_sigprocmask",
|
"rt_sigprocmask",
|
||||||
|
@ -255,6 +341,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"rt_sigreturn",
|
"rt_sigreturn",
|
||||||
"rt_sigsuspend",
|
"rt_sigsuspend",
|
||||||
"rt_sigtimedwait",
|
"rt_sigtimedwait",
|
||||||
|
"rt_sigtimedwait_time64",
|
||||||
"rt_tgsigqueueinfo",
|
"rt_tgsigqueueinfo",
|
||||||
"sched_get_priority_max",
|
"sched_get_priority_max",
|
||||||
"sched_get_priority_min",
|
"sched_get_priority_min",
|
||||||
|
@ -263,6 +350,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"sched_getparam",
|
"sched_getparam",
|
||||||
"sched_getscheduler",
|
"sched_getscheduler",
|
||||||
"sched_rr_get_interval",
|
"sched_rr_get_interval",
|
||||||
|
"sched_rr_get_interval_time64",
|
||||||
"sched_setaffinity",
|
"sched_setaffinity",
|
||||||
"sched_setattr",
|
"sched_setattr",
|
||||||
"sched_setparam",
|
"sched_setparam",
|
||||||
|
@ -274,12 +362,14 @@ func DefaultProfile() *Seccomp {
|
||||||
"semget",
|
"semget",
|
||||||
"semop",
|
"semop",
|
||||||
"semtimedop",
|
"semtimedop",
|
||||||
|
"semtimedop_time64",
|
||||||
"send",
|
"send",
|
||||||
"sendfile",
|
"sendfile",
|
||||||
"sendfile64",
|
"sendfile64",
|
||||||
"sendmmsg",
|
"sendmmsg",
|
||||||
"sendmsg",
|
"sendmsg",
|
||||||
"sendto",
|
"sendto",
|
||||||
|
"set_mempolicy",
|
||||||
"set_robust_list",
|
"set_robust_list",
|
||||||
"set_thread_area",
|
"set_thread_area",
|
||||||
"set_tid_address",
|
"set_tid_address",
|
||||||
|
@ -292,6 +382,7 @@ func DefaultProfile() *Seccomp {
|
||||||
"setgroups",
|
"setgroups",
|
||||||
"setgroups32",
|
"setgroups32",
|
||||||
"setitimer",
|
"setitimer",
|
||||||
|
"setns",
|
||||||
"setpgid",
|
"setpgid",
|
||||||
"setpriority",
|
"setpriority",
|
||||||
"setregid",
|
"setregid",
|
||||||
|
@ -314,8 +405,10 @@ func DefaultProfile() *Seccomp {
|
||||||
"shmget",
|
"shmget",
|
||||||
"shutdown",
|
"shutdown",
|
||||||
"sigaltstack",
|
"sigaltstack",
|
||||||
|
"signal",
|
||||||
"signalfd",
|
"signalfd",
|
||||||
"signalfd4",
|
"signalfd4",
|
||||||
|
"sigprocmask",
|
||||||
"sigreturn",
|
"sigreturn",
|
||||||
"socketcall",
|
"socketcall",
|
||||||
"socketpair",
|
"socketpair",
|
||||||
|
@ -339,10 +432,14 @@ func DefaultProfile() *Seccomp {
|
||||||
"timer_delete",
|
"timer_delete",
|
||||||
"timer_getoverrun",
|
"timer_getoverrun",
|
||||||
"timer_gettime",
|
"timer_gettime",
|
||||||
|
"timer_gettime64",
|
||||||
"timer_settime",
|
"timer_settime",
|
||||||
|
"timer_settime64",
|
||||||
"timerfd_create",
|
"timerfd_create",
|
||||||
"timerfd_gettime",
|
"timerfd_gettime",
|
||||||
|
"timerfd_gettime64",
|
||||||
"timerfd_settime",
|
"timerfd_settime",
|
||||||
|
"timerfd_settime64",
|
||||||
"times",
|
"times",
|
||||||
"tkill",
|
"tkill",
|
||||||
"truncate",
|
"truncate",
|
||||||
|
@ -357,9 +454,9 @@ func DefaultProfile() *Seccomp {
|
||||||
"unshare",
|
"unshare",
|
||||||
"utime",
|
"utime",
|
||||||
"utimensat",
|
"utimensat",
|
||||||
|
"utimensat_time64",
|
||||||
"utimes",
|
"utimes",
|
||||||
"vfork",
|
"vfork",
|
||||||
"vmsplice",
|
|
||||||
"wait4",
|
"wait4",
|
||||||
"waitid",
|
"waitid",
|
||||||
"waitpid",
|
"waitpid",
|
||||||
|
@ -427,6 +524,7 @@ func DefaultProfile() *Seccomp {
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"sync_file_range2",
|
"sync_file_range2",
|
||||||
|
"swapcontext",
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActAllow,
|
||||||
Args: []*Arg{},
|
Args: []*Arg{},
|
||||||
|
@ -438,10 +536,10 @@ func DefaultProfile() *Seccomp {
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"arm_fadvise64_64",
|
"arm_fadvise64_64",
|
||||||
"arm_sync_file_range",
|
"arm_sync_file_range",
|
||||||
"sync_file_range2",
|
|
||||||
"breakpoint",
|
"breakpoint",
|
||||||
"cacheflush",
|
"cacheflush",
|
||||||
"set_tls",
|
"set_tls",
|
||||||
|
"sync_file_range2",
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActAllow,
|
||||||
Args: []*Arg{},
|
Args: []*Arg{},
|
||||||
|
@ -481,6 +579,16 @@ func DefaultProfile() *Seccomp {
|
||||||
Arches: []string{"s390", "s390x"},
|
Arches: []string{"s390", "s390x"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
"riscv_flush_icache",
|
||||||
|
},
|
||||||
|
Action: ActAllow,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Includes: Filter{
|
||||||
|
Arches: []string{"riscv64"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"open_by_handle_at",
|
"open_by_handle_at",
|
||||||
|
@ -491,22 +599,28 @@ func DefaultProfile() *Seccomp {
|
||||||
Caps: []string{"CAP_DAC_READ_SEARCH"},
|
Caps: []string{"CAP_DAC_READ_SEARCH"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
"open_by_handle_at",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_DAC_READ_SEARCH"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"bpf",
|
"bpf",
|
||||||
"clone",
|
|
||||||
"fanotify_init",
|
"fanotify_init",
|
||||||
"lookup_dcookie",
|
"lookup_dcookie",
|
||||||
"mount",
|
|
||||||
"name_to_handle_at",
|
|
||||||
"perf_event_open",
|
|
||||||
"quotactl",
|
"quotactl",
|
||||||
|
"quotactl_fd",
|
||||||
"setdomainname",
|
"setdomainname",
|
||||||
"sethostname",
|
"sethostname",
|
||||||
"setns",
|
"setns",
|
||||||
"umount",
|
|
||||||
"umount2",
|
|
||||||
"unshare",
|
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActAllow,
|
||||||
Args: []*Arg{},
|
Args: []*Arg{},
|
||||||
|
@ -516,53 +630,23 @@ func DefaultProfile() *Seccomp {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"clone",
|
"fanotify_init",
|
||||||
},
|
"lookup_dcookie",
|
||||||
Action: ActAllow,
|
"perf_event_open",
|
||||||
Args: []*Arg{
|
"quotactl",
|
||||||
{
|
"quotactl_fd",
|
||||||
Index: 0,
|
"setdomainname",
|
||||||
Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET,
|
"sethostname",
|
||||||
ValueTwo: 0,
|
"setns",
|
||||||
Op: OpMaskedEqual,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Excludes: Filter{
|
|
||||||
Caps: []string{"CAP_SYS_ADMIN"},
|
|
||||||
Arches: []string{"s390", "s390x"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Names: []string{
|
|
||||||
"clone",
|
|
||||||
},
|
|
||||||
Action: ActAllow,
|
|
||||||
Args: []*Arg{
|
|
||||||
{
|
|
||||||
Index: 1,
|
|
||||||
Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET,
|
|
||||||
ValueTwo: 0,
|
|
||||||
Op: OpMaskedEqual,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Comment: "s390 parameter ordering for clone is different",
|
|
||||||
Includes: Filter{
|
|
||||||
Arches: []string{"s390", "s390x"},
|
|
||||||
},
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
Excludes: Filter{
|
Excludes: Filter{
|
||||||
Caps: []string{"CAP_SYS_ADMIN"},
|
Caps: []string{"CAP_SYS_ADMIN"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Names: []string{
|
|
||||||
"reboot",
|
|
||||||
},
|
|
||||||
Action: ActAllow,
|
|
||||||
Args: []*Arg{},
|
|
||||||
Includes: Filter{
|
|
||||||
Caps: []string{"CAP_SYS_BOOT"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"chroot",
|
"chroot",
|
||||||
|
@ -573,11 +657,23 @@ func DefaultProfile() *Seccomp {
|
||||||
Caps: []string{"CAP_SYS_CHROOT"},
|
Caps: []string{"CAP_SYS_CHROOT"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
"chroot",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_SYS_CHROOT"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"delete_module",
|
"delete_module",
|
||||||
"init_module",
|
|
||||||
"finit_module",
|
"finit_module",
|
||||||
|
"init_module",
|
||||||
"query_module",
|
"query_module",
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActAllow,
|
||||||
|
@ -588,15 +684,17 @@ func DefaultProfile() *Seccomp {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"get_mempolicy",
|
"delete_module",
|
||||||
"mbind",
|
"finit_module",
|
||||||
"name_to_handle_at",
|
"init_module",
|
||||||
"set_mempolicy",
|
"query_module",
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActErrno,
|
||||||
Args: []*Arg{},
|
Errno: "EPERM",
|
||||||
Includes: Filter{
|
ErrnoRet: &eperm,
|
||||||
Caps: []string{"CAP_SYS_NICE"},
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_SYS_MODULE"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -609,12 +707,22 @@ func DefaultProfile() *Seccomp {
|
||||||
Caps: []string{"CAP_SYS_PACCT"},
|
Caps: []string{"CAP_SYS_PACCT"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
"acct",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_SYS_PACCT"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"kcmp",
|
"kcmp",
|
||||||
"process_vm_readv",
|
"process_madvise",
|
||||||
"process_vm_writev",
|
|
||||||
"ptrace",
|
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActAllow,
|
||||||
Args: []*Arg{},
|
Args: []*Arg{},
|
||||||
|
@ -624,8 +732,21 @@ func DefaultProfile() *Seccomp {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"iopl",
|
"kcmp",
|
||||||
|
"process_madvise",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_SYS_PTRACE"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
"ioperm",
|
"ioperm",
|
||||||
|
"iopl",
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActAllow,
|
||||||
Args: []*Arg{},
|
Args: []*Arg{},
|
||||||
|
@ -635,9 +756,23 @@ func DefaultProfile() *Seccomp {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
|
"ioperm",
|
||||||
|
"iopl",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_SYS_RAWIO"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
"clock_settime",
|
||||||
|
"clock_settime64",
|
||||||
"settimeofday",
|
"settimeofday",
|
||||||
"stime",
|
"stime",
|
||||||
"clock_settime",
|
|
||||||
},
|
},
|
||||||
Action: ActAllow,
|
Action: ActAllow,
|
||||||
Args: []*Arg{},
|
Args: []*Arg{},
|
||||||
|
@ -645,6 +780,21 @@ func DefaultProfile() *Seccomp {
|
||||||
Caps: []string{"CAP_SYS_TIME"},
|
Caps: []string{"CAP_SYS_TIME"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
"clock_settime",
|
||||||
|
"clock_settime64",
|
||||||
|
"settimeofday",
|
||||||
|
"stime",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_SYS_TIME"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"vhangup",
|
"vhangup",
|
||||||
|
@ -655,21 +805,34 @@ func DefaultProfile() *Seccomp {
|
||||||
Caps: []string{"CAP_SYS_TTY_CONFIG"},
|
Caps: []string{"CAP_SYS_TTY_CONFIG"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Names: []string{
|
||||||
|
"vhangup",
|
||||||
|
},
|
||||||
|
Action: ActErrno,
|
||||||
|
Errno: "EPERM",
|
||||||
|
ErrnoRet: &eperm,
|
||||||
|
Args: []*Arg{},
|
||||||
|
Excludes: Filter{
|
||||||
|
Caps: []string{"CAP_SYS_TTY_CONFIG"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Names: []string{
|
Names: []string{
|
||||||
"socket",
|
"socket",
|
||||||
},
|
},
|
||||||
Action: ActErrno,
|
Action: ActErrno,
|
||||||
|
Errno: "EINVAL",
|
||||||
ErrnoRet: &einval,
|
ErrnoRet: &einval,
|
||||||
Args: []*Arg{
|
Args: []*Arg{
|
||||||
{
|
{
|
||||||
Index: 0,
|
Index: 0,
|
||||||
Value: syscall.AF_NETLINK,
|
Value: unix.AF_NETLINK,
|
||||||
Op: OpEqualTo,
|
Op: OpEqualTo,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Index: 2,
|
Index: 2,
|
||||||
Value: syscall.NETLINK_AUDIT,
|
Value: unix.NETLINK_AUDIT,
|
||||||
Op: OpEqualTo,
|
Op: OpEqualTo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -685,7 +848,7 @@ func DefaultProfile() *Seccomp {
|
||||||
Args: []*Arg{
|
Args: []*Arg{
|
||||||
{
|
{
|
||||||
Index: 2,
|
Index: 2,
|
||||||
Value: syscall.NETLINK_AUDIT,
|
Value: unix.NETLINK_AUDIT,
|
||||||
Op: OpNotEqual,
|
Op: OpNotEqual,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -701,7 +864,7 @@ func DefaultProfile() *Seccomp {
|
||||||
Args: []*Arg{
|
Args: []*Arg{
|
||||||
{
|
{
|
||||||
Index: 0,
|
Index: 0,
|
||||||
Value: syscall.AF_NETLINK,
|
Value: unix.AF_NETLINK,
|
||||||
Op: OpNotEqual,
|
Op: OpNotEqual,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -717,7 +880,7 @@ func DefaultProfile() *Seccomp {
|
||||||
Args: []*Arg{
|
Args: []*Arg{
|
||||||
{
|
{
|
||||||
Index: 2,
|
Index: 2,
|
||||||
Value: syscall.NETLINK_AUDIT,
|
Value: unix.NETLINK_AUDIT,
|
||||||
Op: OpNotEqual,
|
Op: OpNotEqual,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -734,11 +897,57 @@ func DefaultProfile() *Seccomp {
|
||||||
Caps: []string{"CAP_AUDIT_WRITE"},
|
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{
|
return &Seccomp{
|
||||||
DefaultAction: ActErrno,
|
DefaultAction: ActErrno,
|
||||||
ArchMap: arches(),
|
DefaultErrno: "ENOSYS",
|
||||||
Syscalls: syscalls,
|
DefaultErrnoRet: &enosys,
|
||||||
|
ArchMap: arches(),
|
||||||
|
Syscalls: syscalls,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
//go:build linux && seccomp
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error table
|
||||||
|
var errnoArch = map[string]uint{
|
||||||
|
"EPERM": uint(unix.EPERM),
|
||||||
|
"ENOENT": uint(unix.ENOENT),
|
||||||
|
"ESRCH": uint(unix.ESRCH),
|
||||||
|
"EIO": uint(unix.EIO),
|
||||||
|
"ENXIO": uint(unix.ENXIO),
|
||||||
|
"E2BIG": uint(unix.E2BIG),
|
||||||
|
"ENOEXEC": uint(unix.ENOEXEC),
|
||||||
|
"EBADF": uint(unix.EBADF),
|
||||||
|
"ECHILD": uint(unix.ECHILD),
|
||||||
|
"EDEADLK": uint(unix.EDEADLK),
|
||||||
|
"ENOMEM": uint(unix.ENOMEM),
|
||||||
|
"EACCES": uint(unix.EACCES),
|
||||||
|
"EFAULT": uint(unix.EFAULT),
|
||||||
|
"ENOTBLK": uint(unix.ENOTBLK),
|
||||||
|
"EBUSY": uint(unix.EBUSY),
|
||||||
|
"EEXIST": uint(unix.EEXIST),
|
||||||
|
"EXDEV": uint(unix.EXDEV),
|
||||||
|
"ENODEV": uint(unix.ENODEV),
|
||||||
|
"ENOTDIR": uint(unix.ENOTDIR),
|
||||||
|
"EISDIR": uint(unix.EISDIR),
|
||||||
|
"EINVAL": uint(unix.EINVAL),
|
||||||
|
"ENFILE": uint(unix.ENFILE),
|
||||||
|
"EMFILE": uint(unix.EMFILE),
|
||||||
|
"ENOTTY": uint(unix.ENOTTY),
|
||||||
|
"ETXTBSY": uint(unix.ETXTBSY),
|
||||||
|
"EFBIG": uint(unix.EFBIG),
|
||||||
|
"ENOSPC": uint(unix.ENOSPC),
|
||||||
|
"ESPIPE": uint(unix.ESPIPE),
|
||||||
|
"EROFS": uint(unix.EROFS),
|
||||||
|
"EMLINK": uint(unix.EMLINK),
|
||||||
|
"EPIPE": uint(unix.EPIPE),
|
||||||
|
"EDOM": uint(unix.EDOM),
|
||||||
|
"ERANGE": uint(unix.ERANGE),
|
||||||
|
"EAGAIN": uint(unix.EAGAIN),
|
||||||
|
"EINPROGRESS": uint(unix.EINPROGRESS),
|
||||||
|
"EALREADY": uint(unix.EALREADY),
|
||||||
|
"ENOTSOCK": uint(unix.ENOTSOCK),
|
||||||
|
"EDESTADDRREQ": uint(unix.EDESTADDRREQ),
|
||||||
|
"EMSGSIZE": uint(unix.EMSGSIZE),
|
||||||
|
"EPROTOTYPE": uint(unix.EPROTOTYPE),
|
||||||
|
"ENOPROTOOPT": uint(unix.ENOPROTOOPT),
|
||||||
|
"EPROTONOSUPPORT": uint(unix.EPROTONOSUPPORT),
|
||||||
|
"ESOCKTNOSUPPORT": uint(unix.ESOCKTNOSUPPORT),
|
||||||
|
"EOPNOTSUPP": uint(unix.EOPNOTSUPP),
|
||||||
|
"EPFNOSUPPORT": uint(unix.EPFNOSUPPORT),
|
||||||
|
"EAFNOSUPPORT": uint(unix.EAFNOSUPPORT),
|
||||||
|
"EADDRINUSE": uint(unix.EADDRINUSE),
|
||||||
|
"EADDRNOTAVAIL": uint(unix.EADDRNOTAVAIL),
|
||||||
|
"ENETDOWN": uint(unix.ENETDOWN),
|
||||||
|
"ENETUNREACH": uint(unix.ENETUNREACH),
|
||||||
|
"ENETRESET": uint(unix.ENETRESET),
|
||||||
|
"ECONNABORTED": uint(unix.ECONNABORTED),
|
||||||
|
"ECONNRESET": uint(unix.ECONNRESET),
|
||||||
|
"ENOBUFS": uint(unix.ENOBUFS),
|
||||||
|
"EISCONN": uint(unix.EISCONN),
|
||||||
|
"ENOTCONN": uint(unix.ENOTCONN),
|
||||||
|
"ESHUTDOWN": uint(unix.ESHUTDOWN),
|
||||||
|
"ETOOMANYREFS": uint(unix.ETOOMANYREFS),
|
||||||
|
"ETIMEDOUT": uint(unix.ETIMEDOUT),
|
||||||
|
"ECONNREFUSED": uint(unix.ECONNREFUSED),
|
||||||
|
"ELOOP": uint(unix.ELOOP),
|
||||||
|
"ENAMETOOLONG": uint(unix.ENAMETOOLONG),
|
||||||
|
"EHOSTDOWN": uint(unix.EHOSTDOWN),
|
||||||
|
"EHOSTUNREACH": uint(unix.EHOSTUNREACH),
|
||||||
|
"ENOTEMPTY": uint(unix.ENOTEMPTY),
|
||||||
|
"EUSERS": uint(unix.EUSERS),
|
||||||
|
"EDQUOT": uint(unix.EDQUOT),
|
||||||
|
"ESTALE": uint(unix.ESTALE),
|
||||||
|
"EREMOTE": uint(unix.EREMOTE),
|
||||||
|
"ENOLCK": uint(unix.ENOLCK),
|
||||||
|
"ENOSYS": uint(unix.ENOSYS),
|
||||||
|
"EILSEQ": uint(unix.EILSEQ),
|
||||||
|
"ENOMEDIUM": uint(unix.ENOMEDIUM),
|
||||||
|
"EMEDIUMTYPE": uint(unix.EMEDIUMTYPE),
|
||||||
|
"EOVERFLOW": uint(unix.EOVERFLOW),
|
||||||
|
"ECANCELED": uint(unix.ECANCELED),
|
||||||
|
"EIDRM": uint(unix.EIDRM),
|
||||||
|
"ENOMSG": uint(unix.ENOMSG),
|
||||||
|
"ENOTSUP": uint(unix.ENOTSUP),
|
||||||
|
"EBADMSG": uint(unix.EBADMSG),
|
||||||
|
"ENOTRECOVERABLE": uint(unix.ENOTRECOVERABLE),
|
||||||
|
"EOWNERDEAD": uint(unix.EOWNERDEAD),
|
||||||
|
}
|
|
@ -0,0 +1,240 @@
|
||||||
|
//go:build seccomp
|
||||||
|
|
||||||
|
// NOTE: this package has originally been copied from
|
||||||
|
// github.com/opencontainers/runc and modified to work for other use cases
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: this package has originally been copied from
|
||||||
|
// github.com/opencontainers/runc and modified to work for other use cases
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrSpecNil is a possible return error from BuildFilter() and occurs if
|
||||||
|
// the provided spec is nil.
|
||||||
|
ErrSpecNil = errors.New("spec is nil")
|
||||||
|
|
||||||
|
// ErrSpecEmpty is a possible return error from BuildFilter() and occurs if
|
||||||
|
// the provided spec has neither a DefaultAction nor any syscalls.
|
||||||
|
ErrSpecEmpty = errors.New("spec contains neither a default action nor any syscalls")
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildFilter does a basic validation for the provided seccomp profile
|
||||||
|
// string and returns a filter for it.
|
||||||
|
func BuildFilter(spec *specs.LinuxSeccomp) (*libseccomp.ScmpFilter, error) {
|
||||||
|
// Sanity checking to allow consumers to act accordingly
|
||||||
|
if spec == nil {
|
||||||
|
return nil, ErrSpecNil
|
||||||
|
}
|
||||||
|
if spec.DefaultAction == "" && len(spec.Syscalls) == 0 {
|
||||||
|
return nil, ErrSpecEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
profile, err := specToSeccomp(spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("convert spec to seccomp profile: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultAction, err := toAction(profile.DefaultAction, profile.DefaultErrnoRet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("convert default action %s: %w", profile.DefaultAction, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filter, err := libseccomp.NewFilter(defaultAction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create filter for default action %s: %w", defaultAction, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add extra architectures
|
||||||
|
for _, arch := range spec.Architectures {
|
||||||
|
libseccompArch, err := specArchToLibseccompArch(arch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("convert spec arch: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scmpArch, err := libseccomp.GetArchFromString(libseccompArch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("validate Seccomp architecture %s: %w", arch, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := filter.AddArch(scmpArch); err != nil {
|
||||||
|
return nil, fmt.Errorf("add architecture to seccomp filter: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset no new privs bit
|
||||||
|
if err := filter.SetNoNewPrivsBit(false); err != nil {
|
||||||
|
return nil, fmt.Errorf("set no new privileges flag: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a rule for each syscall
|
||||||
|
for _, call := range profile.Syscalls {
|
||||||
|
if call == nil {
|
||||||
|
return nil, errors.New("encountered nil syscall while initializing seccomp")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = matchSyscall(filter, call); err != nil {
|
||||||
|
return nil, fmt.Errorf("filter matches syscall: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchSyscall(filter *libseccomp.ScmpFilter, call *Syscall) error {
|
||||||
|
if call == nil || filter == nil {
|
||||||
|
return errors.New("cannot use nil as syscall to block")
|
||||||
|
}
|
||||||
|
|
||||||
|
if call.Name == "" {
|
||||||
|
return errors.New("empty string is not a valid syscall")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't resolve the syscall, assume it's not supported on this kernel
|
||||||
|
// Ignore it, don't error out
|
||||||
|
callNum, err := libseccomp.GetSyscallFromName(call.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the call's action to the libseccomp equivalent
|
||||||
|
callAct, err := toAction(call.Action, call.ErrnoRet)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert action %s: %w", call.Action, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unconditional match - just add the rule
|
||||||
|
if len(call.Args) == 0 {
|
||||||
|
if err = filter.AddRule(callNum, callAct); err != nil {
|
||||||
|
return fmt.Errorf("add seccomp filter rule for syscall %s: %w", call.Name, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Linux system calls can have at most 6 arguments
|
||||||
|
const syscallMaxArguments int = 6
|
||||||
|
|
||||||
|
// If two or more arguments have the same condition,
|
||||||
|
// Revert to old behavior, adding each condition as a separate rule
|
||||||
|
argCounts := make([]uint, syscallMaxArguments)
|
||||||
|
conditions := []libseccomp.ScmpCondition{}
|
||||||
|
|
||||||
|
for _, cond := range call.Args {
|
||||||
|
newCond, err := toCondition(cond)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create seccomp syscall condition for syscall %s: %w", call.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
argCounts[cond.Index]++
|
||||||
|
|
||||||
|
conditions = append(conditions, newCond)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMultipleArgs := false
|
||||||
|
for _, count := range argCounts {
|
||||||
|
if count > 1 {
|
||||||
|
hasMultipleArgs = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMultipleArgs {
|
||||||
|
// Revert to old behavior
|
||||||
|
// Add each condition attached to a separate rule
|
||||||
|
for _, cond := range conditions {
|
||||||
|
condArr := []libseccomp.ScmpCondition{cond}
|
||||||
|
|
||||||
|
if err = filter.AddRuleConditional(callNum, callAct, condArr); err != nil {
|
||||||
|
return fmt.Errorf("add seccomp rule for syscall %s: %w", call.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
|
||||||
|
// No conditions share same argument
|
||||||
|
// Use new, proper behavior
|
||||||
|
return fmt.Errorf("add seccomp rule for syscall %s: %w", call.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toAction converts an internal `Action` type to a `libseccomp.ScmpAction`
|
||||||
|
// type.
|
||||||
|
func toAction(act Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
|
||||||
|
switch act {
|
||||||
|
case ActKill:
|
||||||
|
// lint was not passing until this was changed from ActKill to ActKilThread.
|
||||||
|
return libseccomp.ActKillThread, nil
|
||||||
|
case ActKillProcess:
|
||||||
|
return libseccomp.ActKillProcess, nil
|
||||||
|
case ActErrno:
|
||||||
|
if errnoRet != nil {
|
||||||
|
return libseccomp.ActErrno.SetReturnCode(int16(*errnoRet)), nil
|
||||||
|
}
|
||||||
|
return libseccomp.ActErrno.SetReturnCode(int16(unix.EPERM)), nil
|
||||||
|
case ActTrap:
|
||||||
|
return libseccomp.ActTrap, nil
|
||||||
|
case ActAllow:
|
||||||
|
return libseccomp.ActAllow, nil
|
||||||
|
case ActTrace:
|
||||||
|
if errnoRet != nil {
|
||||||
|
return libseccomp.ActTrace.SetReturnCode(int16(*errnoRet)), nil
|
||||||
|
}
|
||||||
|
return libseccomp.ActTrace.SetReturnCode(int16(unix.EPERM)), nil
|
||||||
|
case ActLog:
|
||||||
|
return libseccomp.ActLog, nil
|
||||||
|
default:
|
||||||
|
return libseccomp.ActInvalid, fmt.Errorf("invalid action %s", act)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCondition converts an internal `Arg` type to a `libseccomp.ScmpCondition`
|
||||||
|
// type.
|
||||||
|
func toCondition(arg *Arg) (cond libseccomp.ScmpCondition, err error) {
|
||||||
|
if arg == nil {
|
||||||
|
return cond, errors.New("cannot convert nil to syscall condition")
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err := toCompareOp(arg.Op)
|
||||||
|
if err != nil {
|
||||||
|
return cond, fmt.Errorf("convert compare operator: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
condition, err := libseccomp.MakeCondition(
|
||||||
|
arg.Index, op, arg.Value, arg.ValueTwo,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return cond, fmt.Errorf("make condition: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return condition, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCompareOp converts an internal `Operator` type to a
|
||||||
|
// `libseccomp.ScmpCompareOp`.
|
||||||
|
func toCompareOp(op Operator) (libseccomp.ScmpCompareOp, error) {
|
||||||
|
switch op {
|
||||||
|
case OpEqualTo:
|
||||||
|
return libseccomp.CompareEqual, nil
|
||||||
|
case OpNotEqual:
|
||||||
|
return libseccomp.CompareNotEqual, nil
|
||||||
|
case OpGreaterThan:
|
||||||
|
return libseccomp.CompareGreater, nil
|
||||||
|
case OpGreaterEqual:
|
||||||
|
return libseccomp.CompareGreaterEqual, nil
|
||||||
|
case OpLessThan:
|
||||||
|
return libseccomp.CompareLess, nil
|
||||||
|
case OpLessEqual:
|
||||||
|
return libseccomp.CompareLessOrEqual, nil
|
||||||
|
case OpMaskedEqual:
|
||||||
|
return libseccomp.CompareMaskedEqual, nil
|
||||||
|
default:
|
||||||
|
return libseccomp.CompareInvalid, fmt.Errorf("invalid operator %s", op)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"defaultAction": "SCMP_ACT_ERRNO",
|
"defaultAction": "SCMP_ACT_ERRNO",
|
||||||
|
"defaultErrnoRet": 38,
|
||||||
|
"defaultErrno": "ENOSYS",
|
||||||
"archMap": [
|
"archMap": [
|
||||||
{
|
{
|
||||||
"architecture": "SCMP_ARCH_X86_64",
|
"architecture": "SCMP_ARCH_X86_64",
|
||||||
|
@ -50,6 +52,52 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"syscalls": [
|
"syscalls": [
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"nice",
|
||||||
|
"oldfstat",
|
||||||
|
"oldlstat",
|
||||||
|
"oldolduname",
|
||||||
|
"oldstat",
|
||||||
|
"olduname",
|
||||||
|
"pciconfig_iobase",
|
||||||
|
"pciconfig_read",
|
||||||
|
"pciconfig_write",
|
||||||
|
"sgetmask",
|
||||||
|
"ssetmask",
|
||||||
|
"swapoff",
|
||||||
|
"swapon",
|
||||||
|
"syscall",
|
||||||
|
"sysfs",
|
||||||
|
"uselib",
|
||||||
|
"userfaultfd",
|
||||||
|
"ustat",
|
||||||
|
"vm86",
|
||||||
|
"vm86old",
|
||||||
|
"vmsplice"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"_llseek",
|
"_llseek",
|
||||||
|
@ -67,10 +115,18 @@
|
||||||
"chmod",
|
"chmod",
|
||||||
"chown",
|
"chown",
|
||||||
"chown32",
|
"chown32",
|
||||||
|
"clock_adjtime",
|
||||||
|
"clock_adjtime64",
|
||||||
"clock_getres",
|
"clock_getres",
|
||||||
|
"clock_getres_time64",
|
||||||
"clock_gettime",
|
"clock_gettime",
|
||||||
|
"clock_gettime64",
|
||||||
"clock_nanosleep",
|
"clock_nanosleep",
|
||||||
|
"clock_nanosleep_time64",
|
||||||
|
"clone",
|
||||||
|
"clone3",
|
||||||
"close",
|
"close",
|
||||||
|
"close_range",
|
||||||
"connect",
|
"connect",
|
||||||
"copy_file_range",
|
"copy_file_range",
|
||||||
"creat",
|
"creat",
|
||||||
|
@ -82,6 +138,7 @@
|
||||||
"epoll_ctl",
|
"epoll_ctl",
|
||||||
"epoll_ctl_old",
|
"epoll_ctl_old",
|
||||||
"epoll_pwait",
|
"epoll_pwait",
|
||||||
|
"epoll_pwait2",
|
||||||
"epoll_wait",
|
"epoll_wait",
|
||||||
"epoll_wait_old",
|
"epoll_wait_old",
|
||||||
"eventfd",
|
"eventfd",
|
||||||
|
@ -91,6 +148,7 @@
|
||||||
"exit",
|
"exit",
|
||||||
"exit_group",
|
"exit_group",
|
||||||
"faccessat",
|
"faccessat",
|
||||||
|
"faccessat2",
|
||||||
"fadvise64",
|
"fadvise64",
|
||||||
"fadvise64_64",
|
"fadvise64_64",
|
||||||
"fallocate",
|
"fallocate",
|
||||||
|
@ -98,6 +156,7 @@
|
||||||
"fchdir",
|
"fchdir",
|
||||||
"fchmod",
|
"fchmod",
|
||||||
"fchmodat",
|
"fchmodat",
|
||||||
|
"fchmodat2",
|
||||||
"fchown",
|
"fchown",
|
||||||
"fchown32",
|
"fchown32",
|
||||||
"fchownat",
|
"fchownat",
|
||||||
|
@ -109,7 +168,11 @@
|
||||||
"flock",
|
"flock",
|
||||||
"fork",
|
"fork",
|
||||||
"fremovexattr",
|
"fremovexattr",
|
||||||
|
"fsconfig",
|
||||||
"fsetxattr",
|
"fsetxattr",
|
||||||
|
"fsmount",
|
||||||
|
"fsopen",
|
||||||
|
"fspick",
|
||||||
"fstat",
|
"fstat",
|
||||||
"fstat64",
|
"fstat64",
|
||||||
"fstatat64",
|
"fstatat64",
|
||||||
|
@ -119,7 +182,9 @@
|
||||||
"ftruncate",
|
"ftruncate",
|
||||||
"ftruncate64",
|
"ftruncate64",
|
||||||
"futex",
|
"futex",
|
||||||
|
"futex_time64",
|
||||||
"futimesat",
|
"futimesat",
|
||||||
|
"get_mempolicy",
|
||||||
"get_robust_list",
|
"get_robust_list",
|
||||||
"get_thread_area",
|
"get_thread_area",
|
||||||
"getcpu",
|
"getcpu",
|
||||||
|
@ -169,7 +234,11 @@
|
||||||
"ioprio_get",
|
"ioprio_get",
|
||||||
"ioprio_set",
|
"ioprio_set",
|
||||||
"ipc",
|
"ipc",
|
||||||
|
"keyctl",
|
||||||
"kill",
|
"kill",
|
||||||
|
"landlock_add_rule",
|
||||||
|
"landlock_create_ruleset",
|
||||||
|
"landlock_restrict_self",
|
||||||
"lchown",
|
"lchown",
|
||||||
"lchown32",
|
"lchown32",
|
||||||
"lgetxattr",
|
"lgetxattr",
|
||||||
|
@ -184,7 +253,10 @@
|
||||||
"lstat",
|
"lstat",
|
||||||
"lstat64",
|
"lstat64",
|
||||||
"madvise",
|
"madvise",
|
||||||
|
"mbind",
|
||||||
|
"membarrier",
|
||||||
"memfd_create",
|
"memfd_create",
|
||||||
|
"memfd_secret",
|
||||||
"mincore",
|
"mincore",
|
||||||
"mkdir",
|
"mkdir",
|
||||||
"mkdirat",
|
"mkdirat",
|
||||||
|
@ -196,12 +268,16 @@
|
||||||
"mmap",
|
"mmap",
|
||||||
"mmap2",
|
"mmap2",
|
||||||
"mount",
|
"mount",
|
||||||
|
"mount_setattr",
|
||||||
|
"move_mount",
|
||||||
"mprotect",
|
"mprotect",
|
||||||
"mq_getsetattr",
|
"mq_getsetattr",
|
||||||
"mq_notify",
|
"mq_notify",
|
||||||
"mq_open",
|
"mq_open",
|
||||||
"mq_timedreceive",
|
"mq_timedreceive",
|
||||||
|
"mq_timedreceive_time64",
|
||||||
"mq_timedsend",
|
"mq_timedsend",
|
||||||
|
"mq_timedsend_time64",
|
||||||
"mq_unlink",
|
"mq_unlink",
|
||||||
"mremap",
|
"mremap",
|
||||||
"msgctl",
|
"msgctl",
|
||||||
|
@ -216,18 +292,33 @@
|
||||||
"nanosleep",
|
"nanosleep",
|
||||||
"newfstatat",
|
"newfstatat",
|
||||||
"open",
|
"open",
|
||||||
|
"open_tree",
|
||||||
"openat",
|
"openat",
|
||||||
|
"openat2",
|
||||||
"pause",
|
"pause",
|
||||||
|
"pidfd_getfd",
|
||||||
|
"pidfd_open",
|
||||||
|
"pidfd_send_signal",
|
||||||
"pipe",
|
"pipe",
|
||||||
"pipe2",
|
"pipe2",
|
||||||
|
"pivot_root",
|
||||||
|
"pkey_alloc",
|
||||||
|
"pkey_free",
|
||||||
|
"pkey_mprotect",
|
||||||
"poll",
|
"poll",
|
||||||
"ppoll",
|
"ppoll",
|
||||||
|
"ppoll_time64",
|
||||||
"prctl",
|
"prctl",
|
||||||
"pread64",
|
"pread64",
|
||||||
"preadv",
|
"preadv",
|
||||||
"preadv2",
|
"preadv2",
|
||||||
"prlimit64",
|
"prlimit64",
|
||||||
|
"process_mrelease",
|
||||||
|
"process_vm_readv",
|
||||||
|
"process_vm_writev",
|
||||||
"pselect6",
|
"pselect6",
|
||||||
|
"pselect6_time64",
|
||||||
|
"ptrace",
|
||||||
"pwrite64",
|
"pwrite64",
|
||||||
"pwritev",
|
"pwritev",
|
||||||
"pwritev2",
|
"pwritev2",
|
||||||
|
@ -240,6 +331,7 @@
|
||||||
"recv",
|
"recv",
|
||||||
"recvfrom",
|
"recvfrom",
|
||||||
"recvmmsg",
|
"recvmmsg",
|
||||||
|
"recvmmsg_time64",
|
||||||
"recvmsg",
|
"recvmsg",
|
||||||
"remap_file_pages",
|
"remap_file_pages",
|
||||||
"removexattr",
|
"removexattr",
|
||||||
|
@ -248,6 +340,7 @@
|
||||||
"renameat2",
|
"renameat2",
|
||||||
"restart_syscall",
|
"restart_syscall",
|
||||||
"rmdir",
|
"rmdir",
|
||||||
|
"rseq",
|
||||||
"rt_sigaction",
|
"rt_sigaction",
|
||||||
"rt_sigpending",
|
"rt_sigpending",
|
||||||
"rt_sigprocmask",
|
"rt_sigprocmask",
|
||||||
|
@ -255,6 +348,7 @@
|
||||||
"rt_sigreturn",
|
"rt_sigreturn",
|
||||||
"rt_sigsuspend",
|
"rt_sigsuspend",
|
||||||
"rt_sigtimedwait",
|
"rt_sigtimedwait",
|
||||||
|
"rt_sigtimedwait_time64",
|
||||||
"rt_tgsigqueueinfo",
|
"rt_tgsigqueueinfo",
|
||||||
"sched_get_priority_max",
|
"sched_get_priority_max",
|
||||||
"sched_get_priority_min",
|
"sched_get_priority_min",
|
||||||
|
@ -263,6 +357,7 @@
|
||||||
"sched_getparam",
|
"sched_getparam",
|
||||||
"sched_getscheduler",
|
"sched_getscheduler",
|
||||||
"sched_rr_get_interval",
|
"sched_rr_get_interval",
|
||||||
|
"sched_rr_get_interval_time64",
|
||||||
"sched_setaffinity",
|
"sched_setaffinity",
|
||||||
"sched_setattr",
|
"sched_setattr",
|
||||||
"sched_setparam",
|
"sched_setparam",
|
||||||
|
@ -274,12 +369,14 @@
|
||||||
"semget",
|
"semget",
|
||||||
"semop",
|
"semop",
|
||||||
"semtimedop",
|
"semtimedop",
|
||||||
|
"semtimedop_time64",
|
||||||
"send",
|
"send",
|
||||||
"sendfile",
|
"sendfile",
|
||||||
"sendfile64",
|
"sendfile64",
|
||||||
"sendmmsg",
|
"sendmmsg",
|
||||||
"sendmsg",
|
"sendmsg",
|
||||||
"sendto",
|
"sendto",
|
||||||
|
"set_mempolicy",
|
||||||
"set_robust_list",
|
"set_robust_list",
|
||||||
"set_thread_area",
|
"set_thread_area",
|
||||||
"set_tid_address",
|
"set_tid_address",
|
||||||
|
@ -292,6 +389,7 @@
|
||||||
"setgroups",
|
"setgroups",
|
||||||
"setgroups32",
|
"setgroups32",
|
||||||
"setitimer",
|
"setitimer",
|
||||||
|
"setns",
|
||||||
"setpgid",
|
"setpgid",
|
||||||
"setpriority",
|
"setpriority",
|
||||||
"setregid",
|
"setregid",
|
||||||
|
@ -314,8 +412,10 @@
|
||||||
"shmget",
|
"shmget",
|
||||||
"shutdown",
|
"shutdown",
|
||||||
"sigaltstack",
|
"sigaltstack",
|
||||||
|
"signal",
|
||||||
"signalfd",
|
"signalfd",
|
||||||
"signalfd4",
|
"signalfd4",
|
||||||
|
"sigprocmask",
|
||||||
"sigreturn",
|
"sigreturn",
|
||||||
"socketcall",
|
"socketcall",
|
||||||
"socketpair",
|
"socketpair",
|
||||||
|
@ -339,10 +439,14 @@
|
||||||
"timer_delete",
|
"timer_delete",
|
||||||
"timer_getoverrun",
|
"timer_getoverrun",
|
||||||
"timer_gettime",
|
"timer_gettime",
|
||||||
|
"timer_gettime64",
|
||||||
"timer_settime",
|
"timer_settime",
|
||||||
|
"timer_settime64",
|
||||||
"timerfd_create",
|
"timerfd_create",
|
||||||
"timerfd_gettime",
|
"timerfd_gettime",
|
||||||
|
"timerfd_gettime64",
|
||||||
"timerfd_settime",
|
"timerfd_settime",
|
||||||
|
"timerfd_settime64",
|
||||||
"times",
|
"times",
|
||||||
"tkill",
|
"tkill",
|
||||||
"truncate",
|
"truncate",
|
||||||
|
@ -357,9 +461,9 @@
|
||||||
"unshare",
|
"unshare",
|
||||||
"utime",
|
"utime",
|
||||||
"utimensat",
|
"utimensat",
|
||||||
|
"utimensat_time64",
|
||||||
"utimes",
|
"utimes",
|
||||||
"vfork",
|
"vfork",
|
||||||
"vmsplice",
|
|
||||||
"wait4",
|
"wait4",
|
||||||
"waitid",
|
"waitid",
|
||||||
"waitpid",
|
"waitpid",
|
||||||
|
@ -459,7 +563,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"sync_file_range2"
|
"sync_file_range2",
|
||||||
|
"swapcontext"
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ALLOW",
|
||||||
"args": [],
|
"args": [],
|
||||||
|
@ -475,10 +580,10 @@
|
||||||
"names": [
|
"names": [
|
||||||
"arm_fadvise64_64",
|
"arm_fadvise64_64",
|
||||||
"arm_sync_file_range",
|
"arm_sync_file_range",
|
||||||
"sync_file_range2",
|
|
||||||
"breakpoint",
|
"breakpoint",
|
||||||
"cacheflush",
|
"cacheflush",
|
||||||
"set_tls"
|
"set_tls",
|
||||||
|
"sync_file_range2"
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ALLOW",
|
||||||
"args": [],
|
"args": [],
|
||||||
|
@ -539,6 +644,20 @@
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"excludes": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"riscv_flush_icache"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ALLOW",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {
|
||||||
|
"arches": [
|
||||||
|
"riscv64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"excludes": {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"open_by_handle_at"
|
"open_by_handle_at"
|
||||||
|
@ -553,22 +672,32 @@
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"excludes": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"open_by_handle_at"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_DAC_READ_SEARCH"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"bpf",
|
"bpf",
|
||||||
"clone",
|
|
||||||
"fanotify_init",
|
"fanotify_init",
|
||||||
"lookup_dcookie",
|
"lookup_dcookie",
|
||||||
"mount",
|
|
||||||
"name_to_handle_at",
|
|
||||||
"perf_event_open",
|
|
||||||
"quotactl",
|
"quotactl",
|
||||||
|
"quotactl_fd",
|
||||||
"setdomainname",
|
"setdomainname",
|
||||||
"sethostname",
|
"sethostname",
|
||||||
"setns",
|
"setns"
|
||||||
"umount",
|
|
||||||
"umount2",
|
|
||||||
"unshare"
|
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ALLOW",
|
||||||
"args": [],
|
"args": [],
|
||||||
|
@ -582,68 +711,26 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"clone"
|
"fanotify_init",
|
||||||
],
|
"lookup_dcookie",
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"perf_event_open",
|
||||||
"args": [
|
"quotactl",
|
||||||
{
|
"quotactl_fd",
|
||||||
"index": 0,
|
"setdomainname",
|
||||||
"value": 2080505856,
|
"sethostname",
|
||||||
"valueTwo": 0,
|
"setns"
|
||||||
"op": "SCMP_CMP_MASKED_EQ"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
"comment": "",
|
"comment": "",
|
||||||
"includes": {},
|
"includes": {},
|
||||||
"excludes": {
|
"excludes": {
|
||||||
"caps": [
|
"caps": [
|
||||||
"CAP_SYS_ADMIN"
|
"CAP_SYS_ADMIN"
|
||||||
],
|
|
||||||
"arches": [
|
|
||||||
"s390",
|
|
||||||
"s390x"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"names": [
|
|
||||||
"clone"
|
|
||||||
],
|
|
||||||
"action": "SCMP_ACT_ALLOW",
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"index": 1,
|
|
||||||
"value": 2080505856,
|
|
||||||
"valueTwo": 0,
|
|
||||||
"op": "SCMP_CMP_MASKED_EQ"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"comment": "s390 parameter ordering for clone is different",
|
|
||||||
"includes": {
|
|
||||||
"arches": [
|
|
||||||
"s390",
|
|
||||||
"s390x"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"excludes": {
|
"errnoRet": 1,
|
||||||
"caps": [
|
"errno": "EPERM"
|
||||||
"CAP_SYS_ADMIN"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"names": [
|
|
||||||
"reboot"
|
|
||||||
],
|
|
||||||
"action": "SCMP_ACT_ALLOW",
|
|
||||||
"args": [],
|
|
||||||
"comment": "",
|
|
||||||
"includes": {
|
|
||||||
"caps": [
|
|
||||||
"CAP_SYS_BOOT"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"excludes": {}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
|
@ -659,11 +746,27 @@
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"excludes": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"chroot"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_CHROOT"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"delete_module",
|
"delete_module",
|
||||||
"init_module",
|
|
||||||
"finit_module",
|
"finit_module",
|
||||||
|
"init_module",
|
||||||
"query_module"
|
"query_module"
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ALLOW",
|
||||||
|
@ -678,20 +781,22 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"get_mempolicy",
|
"delete_module",
|
||||||
"mbind",
|
"finit_module",
|
||||||
"name_to_handle_at",
|
"init_module",
|
||||||
"set_mempolicy"
|
"query_module"
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ERRNO",
|
||||||
"args": [],
|
"args": [],
|
||||||
"comment": "",
|
"comment": "",
|
||||||
"includes": {
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
"caps": [
|
"caps": [
|
||||||
"CAP_SYS_NICE"
|
"CAP_SYS_MODULE"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
|
@ -707,12 +812,26 @@
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"excludes": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"acct"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_PACCT"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"kcmp",
|
"kcmp",
|
||||||
"process_vm_readv",
|
"process_madvise"
|
||||||
"process_vm_writev",
|
|
||||||
"ptrace"
|
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ALLOW",
|
||||||
"args": [],
|
"args": [],
|
||||||
|
@ -726,8 +845,25 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"iopl",
|
"kcmp",
|
||||||
"ioperm"
|
"process_madvise"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_PTRACE"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"ioperm",
|
||||||
|
"iopl"
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ALLOW",
|
||||||
"args": [],
|
"args": [],
|
||||||
|
@ -741,9 +877,27 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
|
"ioperm",
|
||||||
|
"iopl"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_RAWIO"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"clock_settime",
|
||||||
|
"clock_settime64",
|
||||||
"settimeofday",
|
"settimeofday",
|
||||||
"stime",
|
"stime"
|
||||||
"clock_settime"
|
|
||||||
],
|
],
|
||||||
"action": "SCMP_ACT_ALLOW",
|
"action": "SCMP_ACT_ALLOW",
|
||||||
"args": [],
|
"args": [],
|
||||||
|
@ -755,6 +909,25 @@
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"excludes": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"clock_settime",
|
||||||
|
"clock_settime64",
|
||||||
|
"settimeofday",
|
||||||
|
"stime"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_TIME"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"vhangup"
|
"vhangup"
|
||||||
|
@ -769,6 +942,22 @@
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"excludes": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"vhangup"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_TTY_CONFIG"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
"socket"
|
"socket"
|
||||||
|
@ -795,7 +984,8 @@
|
||||||
"CAP_AUDIT_WRITE"
|
"CAP_AUDIT_WRITE"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"errnoRet": 22
|
"errnoRet": 22,
|
||||||
|
"errno": "EINVAL"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"names": [
|
"names": [
|
||||||
|
@ -873,6 +1063,68 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"excludes": {}
|
"excludes": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"bpf"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_ADMIN",
|
||||||
|
"CAP_BPF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"bpf"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ALLOW",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_BPF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"excludes": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"perf_event_open"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ERRNO",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {},
|
||||||
|
"excludes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_SYS_ADMIN",
|
||||||
|
"CAP_BPF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"errnoRet": 1,
|
||||||
|
"errno": "EPERM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"perf_event_open"
|
||||||
|
],
|
||||||
|
"action": "SCMP_ACT_ALLOW",
|
||||||
|
"args": [],
|
||||||
|
"comment": "",
|
||||||
|
"includes": {
|
||||||
|
"caps": [
|
||||||
|
"CAP_PERFMON"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"excludes": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
// +build seccomp
|
//go:build seccomp
|
||||||
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Copyright 2013-2018 Docker, Inc.
|
// Copyright 2013-2018 Docker, Inc.
|
||||||
|
|
||||||
package seccomp // import "github.com/seccomp/containers-golang"
|
package seccomp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
libseccomp "github.com/seccomp/libseccomp-golang"
|
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run -tags 'seccomp' generate.go
|
//go:generate go run -tags 'seccomp' generate.go
|
||||||
|
@ -67,6 +67,37 @@ func inSlice(slice []string, s string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getArchitectures(config *Seccomp, newConfig *specs.LinuxSeccomp) error {
|
||||||
|
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
|
||||||
|
return errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if config.Architectures == 0 then libseccomp will figure out the architecture to use
|
||||||
|
if len(config.Architectures) != 0 {
|
||||||
|
for _, a := range config.Architectures {
|
||||||
|
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getErrno(errno string, def *uint) (*uint, error) {
|
||||||
|
if errno == "" {
|
||||||
|
return def, nil
|
||||||
|
}
|
||||||
|
v, err := strconv.ParseUint(errno, 10, 32)
|
||||||
|
if err == nil {
|
||||||
|
v2 := uint(v)
|
||||||
|
return &v2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v2, found := errnoArch[errno]
|
||||||
|
if !found {
|
||||||
|
return nil, fmt.Errorf("unknown errno %s", errno)
|
||||||
|
}
|
||||||
|
return &v2, nil
|
||||||
|
}
|
||||||
|
|
||||||
func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -80,22 +111,22 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
|
||||||
newConfig := &specs.LinuxSeccomp{}
|
newConfig := &specs.LinuxSeccomp{}
|
||||||
|
|
||||||
var arch string
|
var arch string
|
||||||
var native, err = libseccomp.GetNativeArch()
|
native, err := libseccomp.GetNativeArch()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
arch = native.String()
|
arch = native.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
|
if err := getArchitectures(config, newConfig); err != nil {
|
||||||
return nil, errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if config.Architectures == 0 then libseccomp will figure out the architecture to use
|
for _, flag := range config.Flags {
|
||||||
if len(config.Architectures) != 0 {
|
newConfig.Flags = append(newConfig.Flags, specs.LinuxSeccompFlag(flag))
|
||||||
for _, a := range config.Architectures {
|
|
||||||
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newConfig.ListenerPath = config.ListenerPath
|
||||||
|
newConfig.ListenerMetadata = config.ListenerMetadata
|
||||||
|
|
||||||
if len(config.ArchMap) != 0 {
|
if len(config.ArchMap) != 0 {
|
||||||
for _, a := range config.ArchMap {
|
for _, a := range config.ArchMap {
|
||||||
seccompArch, ok := nativeToSeccomp[arch]
|
seccompArch, ok := nativeToSeccomp[arch]
|
||||||
|
@ -113,6 +144,11 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
|
||||||
|
|
||||||
newConfig.DefaultAction = specs.LinuxSeccompAction(config.DefaultAction)
|
newConfig.DefaultAction = specs.LinuxSeccompAction(config.DefaultAction)
|
||||||
|
|
||||||
|
newConfig.DefaultErrnoRet, err = getErrno(config.DefaultErrno, config.DefaultErrnoRet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
// Loop through all syscall blocks and convert them to libcontainer format after filtering them
|
// Loop through all syscall blocks and convert them to libcontainer format after filtering them
|
||||||
for _, call := range config.Syscalls {
|
for _, call := range config.Syscalls {
|
||||||
|
@ -123,7 +159,7 @@ Loop:
|
||||||
}
|
}
|
||||||
if len(call.Excludes.Caps) > 0 {
|
if len(call.Excludes.Caps) > 0 {
|
||||||
for _, c := range call.Excludes.Caps {
|
for _, c := range call.Excludes.Caps {
|
||||||
if inSlice(rs.Process.Capabilities.Bounding, c) {
|
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && inSlice(rs.Process.Capabilities.Bounding, c) {
|
||||||
continue Loop
|
continue Loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +171,7 @@ Loop:
|
||||||
}
|
}
|
||||||
if len(call.Includes.Caps) > 0 {
|
if len(call.Includes.Caps) > 0 {
|
||||||
for _, c := range call.Includes.Caps {
|
for _, c := range call.Includes.Caps {
|
||||||
if !inSlice(rs.Process.Capabilities.Bounding, c) {
|
if rs != nil && rs.Process != nil && rs.Process.Capabilities != nil && !inSlice(rs.Process.Capabilities.Bounding, c) {
|
||||||
continue Loop
|
continue Loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,12 +181,17 @@ Loop:
|
||||||
return nil, errors.New("'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'")
|
return nil, errors.New("'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errno, err := getErrno(call.Errno, call.ErrnoRet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if call.Name != "" {
|
if call.Name != "" {
|
||||||
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, call.ErrnoRet))
|
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, errno))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(call.Names) > 0 {
|
if len(call.Names) > 0 {
|
||||||
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, call.ErrnoRet))
|
newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, errno))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,12 +221,5 @@ func createSpecsSyscall(names []string, action Action, args []*Arg, errnoRet *ui
|
||||||
|
|
||||||
// IsEnabled returns true if seccomp is enabled for the host.
|
// IsEnabled returns true if seccomp is enabled for the host.
|
||||||
func IsEnabled() bool {
|
func IsEnabled() bool {
|
||||||
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
return IsSupported()
|
||||||
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
|
||||||
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
|
||||||
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
// +build !seccomp
|
//go:build !linux || !seccomp
|
||||||
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Copyright 2013-2018 Docker, Inc.
|
// Copyright 2013-2018 Docker, Inc.
|
||||||
|
|
||||||
package seccomp // import "github.com/seccomp/containers-golang"
|
package seccomp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -14,17 +14,12 @@ import (
|
||||||
|
|
||||||
var errNotSupported = errors.New("seccomp not enabled in this build")
|
var errNotSupported = errors.New("seccomp not enabled in this build")
|
||||||
|
|
||||||
// DefaultProfile returns a nil pointer on unsupported systems.
|
// LoadProfile returns an error on unsupported systems
|
||||||
func DefaultProfile() *Seccomp {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadProfile returns an error on unsuppored systems
|
|
||||||
func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
||||||
return nil, errNotSupported
|
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) {
|
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
||||||
return nil, errNotSupported
|
return nil, errNotSupported
|
||||||
}
|
}
|
||||||
|
@ -43,3 +38,9 @@ func LoadProfileFromConfig(config *Seccomp, specgen *specs.Spec) (*specs.LinuxSe
|
||||||
func IsEnabled() bool {
|
func IsEnabled() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSupported returns true if the system has been configured to support
|
||||||
|
// seccomp.
|
||||||
|
func IsSupported() bool {
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
//go:build linux && seccomp
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
supported bool
|
||||||
|
supOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsSupported returns true if the system has been configured to support
|
||||||
|
// seccomp (including the check for CONFIG_SECCOMP_FILTER kernel option).
|
||||||
|
func IsSupported() bool {
|
||||||
|
// Excerpts from prctl(2), section ERRORS:
|
||||||
|
//
|
||||||
|
// EACCES
|
||||||
|
// option is PR_SET_SECCOMP and arg2 is SECCOMP_MODE_FILTER, but
|
||||||
|
// the process does not have the CAP_SYS_ADMIN capability or has
|
||||||
|
// not set the no_new_privs attribute <...>.
|
||||||
|
// <...>
|
||||||
|
// EFAULT
|
||||||
|
// option is PR_SET_SECCOMP, arg2 is SECCOMP_MODE_FILTER, the
|
||||||
|
// system was built with CONFIG_SECCOMP_FILTER, and arg3 is an
|
||||||
|
// invalid address.
|
||||||
|
// <...>
|
||||||
|
// EINVAL
|
||||||
|
// option is PR_SET_SECCOMP or PR_GET_SECCOMP, and the kernel
|
||||||
|
// was not configured with CONFIG_SECCOMP.
|
||||||
|
//
|
||||||
|
// EINVAL
|
||||||
|
// option is PR_SET_SECCOMP, arg2 is SECCOMP_MODE_FILTER,
|
||||||
|
// and the kernel was not configured with CONFIG_SECCOMP_FILTER.
|
||||||
|
// <end of quote>
|
||||||
|
//
|
||||||
|
// Meaning, in case these kernel options are set (this is what we check
|
||||||
|
// for here), we will get some other error (most probably EACCES or
|
||||||
|
// EFAULT). IOW, EINVAL means "seccomp not supported", any other error
|
||||||
|
// means it is supported.
|
||||||
|
|
||||||
|
supOnce.Do(func() {
|
||||||
|
supported = unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0) != unix.EINVAL
|
||||||
|
})
|
||||||
|
return supported
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package seccomp // import "github.com/seccomp/containers-golang"
|
package seccomp
|
||||||
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
@ -7,11 +7,19 @@ package seccomp // import "github.com/seccomp/containers-golang"
|
||||||
// Seccomp represents the config for a seccomp profile for syscall restriction.
|
// Seccomp represents the config for a seccomp profile for syscall restriction.
|
||||||
type Seccomp struct {
|
type Seccomp struct {
|
||||||
DefaultAction Action `json:"defaultAction"`
|
DefaultAction Action `json:"defaultAction"`
|
||||||
|
|
||||||
|
// DefaultErrnoRet is obsolete, please use DefaultErrno
|
||||||
|
DefaultErrnoRet *uint `json:"defaultErrnoRet,omitempty"`
|
||||||
|
DefaultErrno string `json:"defaultErrno,omitempty"`
|
||||||
|
|
||||||
// Architectures is kept to maintain backward compatibility with the old
|
// Architectures is kept to maintain backward compatibility with the old
|
||||||
// seccomp profile.
|
// seccomp profile.
|
||||||
Architectures []Arch `json:"architectures,omitempty"`
|
Architectures []Arch `json:"architectures,omitempty"`
|
||||||
ArchMap []Architecture `json:"archMap,omitempty"`
|
ArchMap []Architecture `json:"archMap,omitempty"`
|
||||||
Syscalls []*Syscall `json:"syscalls"`
|
Syscalls []*Syscall `json:"syscalls"`
|
||||||
|
Flags []string `json:"flags,omitempty"`
|
||||||
|
ListenerPath string `json:"listenerPath,omitempty"`
|
||||||
|
ListenerMetadata string `json:"listenerMetadata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Architecture is used to represent a specific architecture
|
// Architecture is used to represent a specific architecture
|
||||||
|
@ -27,6 +35,7 @@ type Arch string
|
||||||
// Additional architectures permitted to be used for system calls
|
// Additional architectures permitted to be used for system calls
|
||||||
// By default only the native architecture of the kernel is permitted
|
// By default only the native architecture of the kernel is permitted
|
||||||
const (
|
const (
|
||||||
|
ArchNative Arch = "SCMP_ARCH_NATIVE"
|
||||||
ArchX86 Arch = "SCMP_ARCH_X86"
|
ArchX86 Arch = "SCMP_ARCH_X86"
|
||||||
ArchX86_64 Arch = "SCMP_ARCH_X86_64"
|
ArchX86_64 Arch = "SCMP_ARCH_X86_64"
|
||||||
ArchX32 Arch = "SCMP_ARCH_X32"
|
ArchX32 Arch = "SCMP_ARCH_X32"
|
||||||
|
@ -43,6 +52,9 @@ const (
|
||||||
ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE"
|
ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE"
|
||||||
ArchS390 Arch = "SCMP_ARCH_S390"
|
ArchS390 Arch = "SCMP_ARCH_S390"
|
||||||
ArchS390X Arch = "SCMP_ARCH_S390X"
|
ArchS390X Arch = "SCMP_ARCH_S390X"
|
||||||
|
ArchPARISC Arch = "SCMP_ARCH_PARISC"
|
||||||
|
ArchPARISC64 Arch = "SCMP_ARCH_PARISC64"
|
||||||
|
ArchRISCV64 Arch = "SCMP_ARCH_RISCV64"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Action taken upon Seccomp rule match
|
// Action taken upon Seccomp rule match
|
||||||
|
@ -50,11 +62,20 @@ type Action string
|
||||||
|
|
||||||
// Define actions for Seccomp rules
|
// Define actions for Seccomp rules
|
||||||
const (
|
const (
|
||||||
ActKill Action = "SCMP_ACT_KILL"
|
// ActKill results in termination of the thread that made the system call.
|
||||||
ActTrap Action = "SCMP_ACT_TRAP"
|
ActKill Action = "SCMP_ACT_KILL"
|
||||||
ActErrno Action = "SCMP_ACT_ERRNO"
|
// ActKillProcess results in termination of the entire process.
|
||||||
ActTrace Action = "SCMP_ACT_TRACE"
|
ActKillProcess Action = "SCMP_ACT_KILL_PROCESS"
|
||||||
ActAllow Action = "SCMP_ACT_ALLOW"
|
// ActKillThread kills the thread that violated the rule. It is the same as
|
||||||
|
// ActKill. All other threads from the same thread group will continue to
|
||||||
|
// execute.
|
||||||
|
ActKillThread Action = "SCMP_ACT_KILL_THREAD"
|
||||||
|
ActTrap Action = "SCMP_ACT_TRAP"
|
||||||
|
ActErrno Action = "SCMP_ACT_ERRNO"
|
||||||
|
ActTrace Action = "SCMP_ACT_TRACE"
|
||||||
|
ActAllow Action = "SCMP_ACT_ALLOW"
|
||||||
|
ActLog Action = "SCMP_ACT_LOG"
|
||||||
|
ActNotify Action = "SCMP_ACT_NOTIFY"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Operator used to match syscall arguments in Seccomp
|
// Operator used to match syscall arguments in Seccomp
|
||||||
|
@ -94,5 +115,7 @@ type Syscall struct {
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
Includes Filter `json:"includes"`
|
Includes Filter `json:"includes"`
|
||||||
Excludes Filter `json:"excludes"`
|
Excludes Filter `json:"excludes"`
|
||||||
ErrnoRet *uint `json:"errnoRet,omitempty"`
|
// ErrnoRet is obsolete, please use Errno
|
||||||
|
ErrnoRet *uint `json:"errnoRet,omitempty"`
|
||||||
|
Errno string `json:"errno,omitempty"`
|
||||||
}
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
//go:build seccomp
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateProfile does a basic validation for the provided seccomp profile
|
||||||
|
// string.
|
||||||
|
func ValidateProfile(content string) error {
|
||||||
|
profile := &Seccomp{}
|
||||||
|
if err := json.Unmarshal([]byte(content), &profile); err != nil {
|
||||||
|
return fmt.Errorf("decoding seccomp profile: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
spec, err := setupSeccomp(profile, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create seccomp spec: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := BuildFilter(spec); err != nil {
|
||||||
|
return fmt.Errorf("build seccomp filter: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
@ -175,13 +176,13 @@
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2018-2019 github.com/seccomp authors.
|
Copyright 2013-2016 Docker, Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
@ -0,0 +1,19 @@
|
||||||
|
Docker
|
||||||
|
Copyright 2012-2016 Docker, Inc.
|
||||||
|
|
||||||
|
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||||
|
|
||||||
|
This product contains software (https://github.com/kr/pty) developed
|
||||||
|
by Keith Rarick, licensed under the MIT License.
|
||||||
|
|
||||||
|
The following is courtesy of our legal counsel:
|
||||||
|
|
||||||
|
|
||||||
|
Use and transfer of Docker may be subject to certain restrictions by the
|
||||||
|
United States and other governments.
|
||||||
|
It is your responsibility to ensure that your use and/or transfer does not
|
||||||
|
violate applicable laws.
|
||||||
|
|
||||||
|
For more information, please see https://www.bis.doc.gov
|
||||||
|
|
||||||
|
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
38
vendor/github.com/containers/storage/pkg/fileutils/exists_freebsd.go
generated
vendored
Normal file
38
vendor/github.com/containers/storage/pkg/fileutils/exists_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exists checks whether a file or directory exists at the given path.
|
||||||
|
// If the path is a symlink, the symlink is followed.
|
||||||
|
func Exists(path string) error {
|
||||||
|
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
|
||||||
|
// simply checking the existence of a file.
|
||||||
|
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, 0)
|
||||||
|
if err != nil {
|
||||||
|
return &os.PathError{Op: "faccessat", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lexists checks whether a file or directory exists at the given path.
|
||||||
|
// If the path is a symlink, the symlink itself is checked.
|
||||||
|
func Lexists(path string) error {
|
||||||
|
// FreeBSD before 15.0 does not support the AT_SYMLINK_NOFOLLOW flag for
|
||||||
|
// faccessat. In this case, the call to faccessat will return EINVAL and
|
||||||
|
// we fall back to using Lstat.
|
||||||
|
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, syscall.EINVAL) {
|
||||||
|
_, err = os.Lstat(path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &os.PathError{Op: "faccessat", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
//go:build !windows && !freebsd
|
||||||
|
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exists checks whether a file or directory exists at the given path.
|
||||||
|
// If the path is a symlink, the symlink is followed.
|
||||||
|
func Exists(path string) error {
|
||||||
|
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
|
||||||
|
// simply checking the existence of a file.
|
||||||
|
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_EACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return &os.PathError{Op: "faccessat", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lexists checks whether a file or directory exists at the given path.
|
||||||
|
// If the path is a symlink, the symlink itself is checked.
|
||||||
|
func Lexists(path string) error {
|
||||||
|
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
|
||||||
|
// simply checking the existence of a file.
|
||||||
|
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW|unix.AT_EACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return &os.PathError{Op: "faccessat", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
18
vendor/github.com/containers/storage/pkg/fileutils/exists_windows.go
generated
vendored
Normal file
18
vendor/github.com/containers/storage/pkg/fileutils/exists_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exists checks whether a file or directory exists at the given path.
|
||||||
|
func Exists(path string) error {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lexists checks whether a file or directory exists at the given path, without
|
||||||
|
// resolving symlinks
|
||||||
|
func Lexists(path string) error {
|
||||||
|
_, err := os.Lstat(path)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,371 @@
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"text/scanner"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PatternMatcher allows checking paths against a list of patterns
|
||||||
|
type PatternMatcher struct {
|
||||||
|
patterns []*Pattern
|
||||||
|
exclusions bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPatternMatcher creates a new matcher object for specific patterns that can
|
||||||
|
// be used later to match against patterns against paths
|
||||||
|
func NewPatternMatcher(patterns []string) (*PatternMatcher, error) {
|
||||||
|
pm := &PatternMatcher{
|
||||||
|
patterns: make([]*Pattern, 0, len(patterns)),
|
||||||
|
}
|
||||||
|
for _, p := range patterns {
|
||||||
|
// Eliminate leading and trailing whitespace.
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if p == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p = filepath.Clean(p)
|
||||||
|
newp := &Pattern{}
|
||||||
|
if p[0] == '!' {
|
||||||
|
if len(p) == 1 {
|
||||||
|
return nil, errors.New("illegal exclusion pattern: \"!\"")
|
||||||
|
}
|
||||||
|
newp.exclusion = true
|
||||||
|
p = strings.TrimPrefix(filepath.Clean(p[1:]), "/")
|
||||||
|
pm.exclusions = true
|
||||||
|
}
|
||||||
|
// Do some syntax checking on the pattern.
|
||||||
|
// filepath's Match() has some really weird rules that are inconsistent
|
||||||
|
// so instead of trying to dup their logic, just call Match() for its
|
||||||
|
// error state and if there is an error in the pattern return it.
|
||||||
|
// If this becomes an issue we can remove this since its really only
|
||||||
|
// needed in the error (syntax) case - which isn't really critical.
|
||||||
|
if _, err := filepath.Match(p, "."); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newp.cleanedPattern = p
|
||||||
|
newp.dirs = strings.Split(p, string(os.PathSeparator))
|
||||||
|
pm.patterns = append(pm.patterns, newp)
|
||||||
|
}
|
||||||
|
return pm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Please use the `MatchesResult` method instead.
|
||||||
|
// Matches matches path against all the patterns. Matches is not safe to be
|
||||||
|
// called concurrently
|
||||||
|
func (pm *PatternMatcher) Matches(file string) (bool, error) {
|
||||||
|
matched := false
|
||||||
|
file = filepath.FromSlash(file)
|
||||||
|
|
||||||
|
for _, pattern := range pm.patterns {
|
||||||
|
negative := false
|
||||||
|
|
||||||
|
if pattern.exclusion {
|
||||||
|
negative = true
|
||||||
|
}
|
||||||
|
|
||||||
|
match, err := pattern.match(file)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if match {
|
||||||
|
matched = !negative
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
logrus.Debugf("Skipping excluded path: %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatchResult struct {
|
||||||
|
isMatched bool
|
||||||
|
matches, excludes uint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Excludes returns true if the overall result is matched
|
||||||
|
func (m *MatchResult) IsMatched() bool {
|
||||||
|
return m.isMatched
|
||||||
|
}
|
||||||
|
|
||||||
|
// Excludes returns the amount of matches of an MatchResult
|
||||||
|
func (m *MatchResult) Matches() uint {
|
||||||
|
return m.matches
|
||||||
|
}
|
||||||
|
|
||||||
|
// Excludes returns the amount of excludes of an MatchResult
|
||||||
|
func (m *MatchResult) Excludes() uint {
|
||||||
|
return m.excludes
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesResult verifies the provided filepath against all patterns.
|
||||||
|
// It returns the `*MatchResult` result for the patterns on success, otherwise
|
||||||
|
// an error. This method is not safe to be called concurrently.
|
||||||
|
func (pm *PatternMatcher) MatchesResult(file string) (res *MatchResult, err error) {
|
||||||
|
file = filepath.FromSlash(file)
|
||||||
|
res = &MatchResult{false, 0, 0}
|
||||||
|
|
||||||
|
for _, pattern := range pm.patterns {
|
||||||
|
negative := false
|
||||||
|
|
||||||
|
if pattern.exclusion {
|
||||||
|
negative = true
|
||||||
|
}
|
||||||
|
|
||||||
|
match, err := pattern.match(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if match {
|
||||||
|
res.isMatched = !negative
|
||||||
|
if negative {
|
||||||
|
res.excludes++
|
||||||
|
} else {
|
||||||
|
res.matches++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.matches > 0 {
|
||||||
|
logrus.Debugf("Skipping excluded path: %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMatch verifies the provided filepath against all patterns and returns true
|
||||||
|
// if it matches. A match is valid if the last match is a positive one.
|
||||||
|
// It returns an error on failure and is not safe to be called concurrently.
|
||||||
|
func (pm *PatternMatcher) IsMatch(file string) (matched bool, err error) {
|
||||||
|
res, err := pm.MatchesResult(file)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return res.isMatched, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclusions returns true if any of the patterns define exclusions
|
||||||
|
func (pm *PatternMatcher) Exclusions() bool {
|
||||||
|
return pm.exclusions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patterns returns array of active patterns
|
||||||
|
func (pm *PatternMatcher) Patterns() []*Pattern {
|
||||||
|
return pm.patterns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern defines a single regexp used to filter file paths.
|
||||||
|
type Pattern struct {
|
||||||
|
cleanedPattern string
|
||||||
|
dirs []string
|
||||||
|
regexp *regexp.Regexp
|
||||||
|
exclusion bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pattern) String() string {
|
||||||
|
return p.cleanedPattern
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclusion returns true if this pattern defines exclusion
|
||||||
|
func (p *Pattern) Exclusion() bool {
|
||||||
|
return p.exclusion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pattern) match(path string) (bool, error) {
|
||||||
|
if p.regexp == nil {
|
||||||
|
if err := p.compile(); err != nil {
|
||||||
|
return false, filepath.ErrBadPattern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b := p.regexp.MatchString(path)
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pattern) compile() error {
|
||||||
|
regStr := "^"
|
||||||
|
pattern := p.cleanedPattern
|
||||||
|
// Go through the pattern and convert it to a regexp.
|
||||||
|
// We use a scanner so we can support utf-8 chars.
|
||||||
|
var scan scanner.Scanner
|
||||||
|
scan.Init(strings.NewReader(pattern))
|
||||||
|
|
||||||
|
sl := string(os.PathSeparator)
|
||||||
|
escSL := sl
|
||||||
|
const bs = `\`
|
||||||
|
if sl == bs {
|
||||||
|
escSL += bs
|
||||||
|
}
|
||||||
|
|
||||||
|
for scan.Peek() != scanner.EOF {
|
||||||
|
ch := scan.Next()
|
||||||
|
|
||||||
|
if ch == '*' {
|
||||||
|
if scan.Peek() == '*' {
|
||||||
|
// is some flavor of "**"
|
||||||
|
scan.Next()
|
||||||
|
|
||||||
|
// Treat **/ as ** so eat the "/"
|
||||||
|
if string(scan.Peek()) == sl {
|
||||||
|
scan.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if scan.Peek() == scanner.EOF {
|
||||||
|
// is "**EOF" - to align with .gitignore just accept all
|
||||||
|
regStr += ".*"
|
||||||
|
} else {
|
||||||
|
// is "**"
|
||||||
|
// Note that this allows for any # of /'s (even 0) because
|
||||||
|
// the .* will eat everything, even /'s
|
||||||
|
regStr += "(.*" + escSL + ")?"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// is "*" so map it to anything but "/"
|
||||||
|
regStr += "[^" + escSL + "]*"
|
||||||
|
}
|
||||||
|
} else if ch == '?' {
|
||||||
|
// "?" is any char except "/"
|
||||||
|
regStr += "[^" + escSL + "]"
|
||||||
|
} else if ch == '.' || ch == '$' {
|
||||||
|
// Escape some regexp special chars that have no meaning
|
||||||
|
// in golang's filepath.Match
|
||||||
|
regStr += bs + string(ch)
|
||||||
|
} else if ch == '\\' {
|
||||||
|
// escape next char.
|
||||||
|
if sl == bs {
|
||||||
|
// On windows map "\" to "\\", meaning an escaped backslash,
|
||||||
|
// and then just continue because filepath.Match on
|
||||||
|
// Windows doesn't allow escaping at all
|
||||||
|
regStr += escSL
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if scan.Peek() != scanner.EOF {
|
||||||
|
regStr += bs + string(scan.Next())
|
||||||
|
} else {
|
||||||
|
return filepath.ErrBadPattern
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
regStr += string(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
regStr += "(" + escSL + ".*)?$"
|
||||||
|
|
||||||
|
re, err := regexp.Compile(regStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.regexp = re
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches returns true if file matches any of the patterns
|
||||||
|
// and isn't excluded by any of the subsequent patterns.
|
||||||
|
func Matches(file string, patterns []string) (bool, error) {
|
||||||
|
pm, err := NewPatternMatcher(patterns)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
file = filepath.Clean(file)
|
||||||
|
|
||||||
|
if file == "." {
|
||||||
|
// Don't let them exclude everything, kind of silly.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pm.IsMatch(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyFile copies from src to dst until either EOF is reached
|
||||||
|
// on src or an error occurs. It verifies src exists and removes
|
||||||
|
// the dst if it exists.
|
||||||
|
func CopyFile(src, dst string) (int64, error) {
|
||||||
|
cleanSrc := filepath.Clean(src)
|
||||||
|
cleanDst := filepath.Clean(dst)
|
||||||
|
if cleanSrc == cleanDst {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
sf, err := os.Open(cleanSrc)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer sf.Close()
|
||||||
|
if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
df, err := os.Create(cleanDst)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer df.Close()
|
||||||
|
return io.Copy(df, sf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadSymlinkedDirectory returns the target directory of a symlink.
|
||||||
|
// The target of the symbolic link may not be a file.
|
||||||
|
func ReadSymlinkedDirectory(path string) (string, error) {
|
||||||
|
var realPath string
|
||||||
|
var err error
|
||||||
|
if realPath, err = filepath.Abs(path); err != nil {
|
||||||
|
return "", fmt.Errorf("unable to get absolute path for %s: %w", path, err)
|
||||||
|
}
|
||||||
|
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to canonicalise path for %s: %w", path, err)
|
||||||
|
}
|
||||||
|
realPathInfo, err := os.Stat(realPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to stat target '%s' of '%s': %w", realPath, path, err)
|
||||||
|
}
|
||||||
|
if !realPathInfo.Mode().IsDir() {
|
||||||
|
return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
|
||||||
|
}
|
||||||
|
return realPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadSymlinkedPath returns the target directory of a symlink.
|
||||||
|
// The target of the symbolic link can be a file and a directory.
|
||||||
|
func ReadSymlinkedPath(path string) (realPath string, err error) {
|
||||||
|
if realPath, err = filepath.Abs(path); err != nil {
|
||||||
|
return "", fmt.Errorf("unable to get absolute path for %q: %w", path, err)
|
||||||
|
}
|
||||||
|
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to canonicalise path for %q: %w", path, err)
|
||||||
|
}
|
||||||
|
if err := Exists(realPath); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to stat target %q of %q: %w", realPath, path, err)
|
||||||
|
}
|
||||||
|
return realPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateIfNotExists creates a file or a directory only if it does not already exist.
|
||||||
|
func CreateIfNotExists(path string, isDir bool) error {
|
||||||
|
if err := Exists(path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if isDir {
|
||||||
|
return os.MkdirAll(path, 0o755)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(path, os.O_CREATE, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
27
vendor/github.com/containers/storage/pkg/fileutils/fileutils_darwin.go
generated
vendored
Normal file
27
vendor/github.com/containers/storage/pkg/fileutils/fileutils_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetTotalUsedFds returns the number of used File Descriptors by
|
||||||
|
// executing `lsof -p PID`
|
||||||
|
func GetTotalUsedFds() int {
|
||||||
|
pid := os.Getpid()
|
||||||
|
|
||||||
|
cmd := exec.Command("lsof", "-p", strconv.Itoa(pid))
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStr := strings.TrimSpace(string(output))
|
||||||
|
|
||||||
|
fds := strings.Split(outputStr, "\n")
|
||||||
|
|
||||||
|
return len(fds) - 1
|
||||||
|
}
|
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_solaris.go
generated
vendored
Normal file
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
// GetTotalUsedFds Returns the number of used File Descriptors.
|
||||||
|
// On Solaris these limits are per process and not systemwide
|
||||||
|
func GetTotalUsedFds() int {
|
||||||
|
return -1
|
||||||
|
}
|
21
vendor/github.com/containers/storage/pkg/fileutils/fileutils_unix.go
generated
vendored
Normal file
21
vendor/github.com/containers/storage/pkg/fileutils/fileutils_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//go:build linux || freebsd
|
||||||
|
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetTotalUsedFds Returns the number of used File Descriptors by
|
||||||
|
// reading it via /proc filesystem.
|
||||||
|
func GetTotalUsedFds() int {
|
||||||
|
if fds, err := os.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
|
||||||
|
logrus.Errorf("%v", err)
|
||||||
|
} else {
|
||||||
|
return len(fds)
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_windows.go
generated
vendored
Normal file
7
vendor/github.com/containers/storage/pkg/fileutils/fileutils_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
// GetTotalUsedFds Returns the number of used File Descriptors. Not supported
|
||||||
|
// on Windows.
|
||||||
|
func GetTotalUsedFds() int {
|
||||||
|
return -1
|
||||||
|
}
|
20
vendor/github.com/containers/storage/pkg/fileutils/reflink_linux.go
generated
vendored
Normal file
20
vendor/github.com/containers/storage/pkg/fileutils/reflink_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReflinkOrCopy attempts to reflink the source to the destination fd.
|
||||||
|
// If reflinking fails or is unsupported, it falls back to io.Copy().
|
||||||
|
func ReflinkOrCopy(src, dst *os.File) error {
|
||||||
|
err := unix.IoctlFileClone(int(dst.Fd()), int(src.Fd()))
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(dst, src)
|
||||||
|
return err
|
||||||
|
}
|
15
vendor/github.com/containers/storage/pkg/fileutils/reflink_unsupported.go
generated
vendored
Normal file
15
vendor/github.com/containers/storage/pkg/fileutils/reflink_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReflinkOrCopy attempts to reflink the source to the destination fd.
|
||||||
|
// If reflinking fails or is unsupported, it falls back to io.Copy().
|
||||||
|
func ReflinkOrCopy(src, dst *os.File) error {
|
||||||
|
_, err := io.Copy(dst, src)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,620 @@
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/system"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IDMap contains a single entry for user namespace range remapping. An array
|
||||||
|
// of IDMap entries represents the structure that will be provided to the Linux
|
||||||
|
// kernel for creating a user namespace.
|
||||||
|
type IDMap struct {
|
||||||
|
ContainerID int `json:"container_id"`
|
||||||
|
HostID int `json:"host_id"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type subIDRange struct {
|
||||||
|
Start int
|
||||||
|
Length int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ranges []subIDRange
|
||||||
|
|
||||||
|
func (e ranges) Len() int { return len(e) }
|
||||||
|
func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|
||||||
|
func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start }
|
||||||
|
|
||||||
|
const (
|
||||||
|
subuidFileName string = "/etc/subuid"
|
||||||
|
subgidFileName string = "/etc/subgid"
|
||||||
|
ContainersOverrideXattr = "user.containers.override_stat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MkdirAllAs creates a directory (include any along the path) and then modifies
|
||||||
|
// ownership to the requested uid/gid. If the directory already exists, this
|
||||||
|
// function will still change ownership to the requested uid/gid pair.
|
||||||
|
// Deprecated: Use MkdirAllAndChown
|
||||||
|
func MkdirAllAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
|
||||||
|
return mkdirAs(path, mode, ownerUID, ownerGID, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAs creates a directory and then modifies ownership to the requested uid/gid.
|
||||||
|
// If the directory already exists, this function still changes ownership
|
||||||
|
// Deprecated: Use MkdirAndChown with a IDPair
|
||||||
|
func MkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
|
||||||
|
return mkdirAs(path, mode, ownerUID, ownerGID, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
||||||
|
// ownership to the requested uid/gid. If the directory already exists, this
|
||||||
|
// function will still change ownership to the requested uid/gid pair.
|
||||||
|
func MkdirAllAndChown(path string, mode os.FileMode, ids IDPair) error {
|
||||||
|
return mkdirAs(path, mode, ids.UID, ids.GID, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
|
||||||
|
// If the directory already exists, this function still changes ownership
|
||||||
|
func MkdirAndChown(path string, mode os.FileMode, ids IDPair) error {
|
||||||
|
return mkdirAs(path, mode, ids.UID, ids.GID, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
|
||||||
|
// ownership ONLY of newly created directories to the requested uid/gid. If the
|
||||||
|
// directories along the path exist, no change of ownership will be performed
|
||||||
|
func MkdirAllAndChownNew(path string, mode os.FileMode, ids IDPair) error {
|
||||||
|
return mkdirAs(path, mode, ids.UID, ids.GID, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
var uid, gid int
|
||||||
|
var err error
|
||||||
|
if len(uidMap) == 1 && uidMap[0].Size == 1 {
|
||||||
|
uid = uidMap[0].HostID
|
||||||
|
} else {
|
||||||
|
uid, err = RawToHost(0, uidMap)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(gidMap) == 1 && gidMap[0].Size == 1 {
|
||||||
|
gid = gidMap[0].HostID
|
||||||
|
} else {
|
||||||
|
gid, err = RawToHost(0, gidMap)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uid, gid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawToContainer 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.
|
||||||
|
//
|
||||||
|
// If you wish to map a (uid,gid) combination you should use the corresponding
|
||||||
|
// IDMappings methods, which ensure that you are mapping the correct ID against
|
||||||
|
// the correct mapping.
|
||||||
|
func RawToContainer(hostID int, idMap []IDMap) (int, error) {
|
||||||
|
if idMap == nil {
|
||||||
|
return hostID, nil
|
||||||
|
}
|
||||||
|
for _, m := range idMap {
|
||||||
|
if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) {
|
||||||
|
contID := m.ContainerID + (hostID - m.HostID)
|
||||||
|
return contID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, fmt.Errorf("host ID %d cannot be mapped to a container ID", hostID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawToHost 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.
|
||||||
|
//
|
||||||
|
// If you wish to map a (uid,gid) combination you should use the corresponding
|
||||||
|
// IDMappings methods, which ensure that you are mapping the correct ID against
|
||||||
|
// the correct mapping.
|
||||||
|
func RawToHost(contID int, idMap []IDMap) (int, error) {
|
||||||
|
if idMap == nil {
|
||||||
|
return contID, nil
|
||||||
|
}
|
||||||
|
for _, m := range idMap {
|
||||||
|
if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) {
|
||||||
|
hostID := m.HostID + (contID - m.ContainerID)
|
||||||
|
return hostID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, fmt.Errorf("container ID %d cannot be mapped to a host ID", contID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDPair is a UID and GID pair
|
||||||
|
type IDPair struct {
|
||||||
|
UID int
|
||||||
|
GID int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDMappings contains a mappings of UIDs and GIDs
|
||||||
|
type IDMappings struct {
|
||||||
|
uids []IDMap
|
||||||
|
gids []IDMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIDMappings takes a requested user and group name and
|
||||||
|
// using the data from /etc/sub{uid,gid} ranges, creates the
|
||||||
|
// proper uid and gid remapping ranges for that user/group pair
|
||||||
|
func NewIDMappings(username, groupname string) (*IDMappings, error) {
|
||||||
|
subuidRanges, err := readSubuid(username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
subgidRanges, err := readSubgid(groupname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(subuidRanges) == 0 {
|
||||||
|
return nil, fmt.Errorf("no subuid ranges found for user %q in %s", username, subuidFileName)
|
||||||
|
}
|
||||||
|
if len(subgidRanges) == 0 {
|
||||||
|
return nil, fmt.Errorf("no subgid ranges found for group %q in %s", groupname, subgidFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &IDMappings{
|
||||||
|
uids: createIDMap(subuidRanges),
|
||||||
|
gids: createIDMap(subgidRanges),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIDMappingsFromMaps creates a new mapping from two slices
|
||||||
|
// Deprecated: this is a temporary shim while transitioning to IDMapping
|
||||||
|
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IDMappings {
|
||||||
|
return &IDMappings{uids: uids, gids: gids}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *IDMappings) RootPair() IDPair {
|
||||||
|
uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
|
||||||
|
return IDPair{UID: uid, GID: gid}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToHost returns the host UID and GID for the container uid, gid.
|
||||||
|
func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) {
|
||||||
|
var err error
|
||||||
|
var target IDPair
|
||||||
|
|
||||||
|
target.UID, err = RawToHost(pair.UID, i.uids)
|
||||||
|
if err != nil {
|
||||||
|
return target, err
|
||||||
|
}
|
||||||
|
|
||||||
|
target.GID, err = RawToHost(pair.GID, i.gids)
|
||||||
|
return target, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
overflowUIDOnce sync.Once
|
||||||
|
overflowGIDOnce sync.Once
|
||||||
|
overflowUID int
|
||||||
|
overflowGID int
|
||||||
|
)
|
||||||
|
|
||||||
|
// getOverflowUID returns the UID mapped to the overflow user
|
||||||
|
func getOverflowUID() int {
|
||||||
|
overflowUIDOnce.Do(func() {
|
||||||
|
// 65534 is the value on older kernels where /proc/sys/kernel/overflowuid is not present
|
||||||
|
overflowUID = 65534
|
||||||
|
if content, err := os.ReadFile("/proc/sys/kernel/overflowuid"); err == nil {
|
||||||
|
if tmp, err := strconv.Atoi(string(content)); err == nil {
|
||||||
|
overflowUID = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return overflowUID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
overflowGID = 65534
|
||||||
|
if content, err := os.ReadFile("/proc/sys/kernel/overflowgid"); err == nil {
|
||||||
|
if tmp, err := strconv.Atoi(string(content)); err == nil {
|
||||||
|
overflowGID = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return overflowGID
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// If the mapping is not possible because the target ID is not mapped into
|
||||||
|
// the namespace, then the overflow ID is used.
|
||||||
|
func (i *IDMappings) ToHostOverflow(pair IDPair) (IDPair, error) {
|
||||||
|
var err error
|
||||||
|
target := i.RootPair()
|
||||||
|
|
||||||
|
if pair.UID != target.UID {
|
||||||
|
target.UID, err = RawToHost(pair.UID, i.uids)
|
||||||
|
if err != nil {
|
||||||
|
target.UID = getOverflowUID()
|
||||||
|
logrus.Debugf("Failed to map UID %v to the target mapping, using the overflow ID %v", pair.UID, target.UID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pair.GID != target.GID {
|
||||||
|
target.GID, err = RawToHost(pair.GID, i.gids)
|
||||||
|
if err != nil {
|
||||||
|
target.GID = getOverflowGID()
|
||||||
|
logrus.Debugf("Failed to map GID %v to the target mapping, using the overflow ID %v", pair.GID, target.GID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToContainer returns the container UID and GID for the host uid and gid
|
||||||
|
func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) {
|
||||||
|
uid, err := RawToContainer(pair.UID, i.uids)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
gid, err := RawToContainer(pair.GID, i.gids)
|
||||||
|
return uid, gid, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns true if there are no id mappings
|
||||||
|
func (i *IDMappings) Empty() bool {
|
||||||
|
return len(i.uids) == 0 && len(i.gids) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UIDs return the UID mapping
|
||||||
|
// TODO: remove this once everything has been refactored to use pairs
|
||||||
|
func (i *IDMappings) UIDs() []IDMap {
|
||||||
|
return i.uids
|
||||||
|
}
|
||||||
|
|
||||||
|
// GIDs return the UID mapping
|
||||||
|
// TODO: remove this once everything has been refactored to use pairs
|
||||||
|
func (i *IDMappings) GIDs() []IDMap {
|
||||||
|
return i.gids
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIDMap(subidRanges ranges) []IDMap {
|
||||||
|
idMap := []IDMap{}
|
||||||
|
|
||||||
|
// sort the ranges by lowest ID first
|
||||||
|
sort.Sort(subidRanges)
|
||||||
|
containerID := 0
|
||||||
|
for _, idrange := range subidRanges {
|
||||||
|
idMap = append(idMap, IDMap{
|
||||||
|
ContainerID: containerID,
|
||||||
|
HostID: idrange.Start,
|
||||||
|
Size: idrange.Length,
|
||||||
|
})
|
||||||
|
containerID = containerID + idrange.Length
|
||||||
|
}
|
||||||
|
return idMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid)
|
||||||
|
// and return all found ranges for a specified username. If the special value
|
||||||
|
// "ALL" is supplied for username, then all ranges in the file will be returned
|
||||||
|
func parseSubidFile(path, username string) (ranges, error) {
|
||||||
|
var (
|
||||||
|
rangeList ranges
|
||||||
|
uidstr string
|
||||||
|
)
|
||||||
|
if u, err := user.Lookup(username); err == nil {
|
||||||
|
uidstr = u.Uid
|
||||||
|
}
|
||||||
|
|
||||||
|
subidFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return rangeList, err
|
||||||
|
}
|
||||||
|
defer subidFile.Close()
|
||||||
|
|
||||||
|
s := bufio.NewScanner(subidFile)
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return rangeList, err
|
||||||
|
}
|
||||||
|
|
||||||
|
text := strings.TrimSpace(s.Text())
|
||||||
|
if text == "" || strings.HasPrefix(text, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Split(text, ":")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return rangeList, fmt.Errorf("cannot parse subuid/gid information: Format not correct for %s file", path)
|
||||||
|
}
|
||||||
|
if parts[0] == username || username == "ALL" || (parts[0] == uidstr && parts[0] != "") {
|
||||||
|
startid, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return rangeList, fmt.Errorf("string to int conversion failed during subuid/gid parsing of %s: %w", path, err)
|
||||||
|
}
|
||||||
|
length, err := strconv.Atoi(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return rangeList, fmt.Errorf("string to int conversion failed during subuid/gid parsing of %s: %w", path, err)
|
||||||
|
}
|
||||||
|
rangeList = append(rangeList, subIDRange{startid, length})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rangeList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChownErr(err error, name string, uid, gid int) error {
|
||||||
|
var e *os.PathError
|
||||||
|
if errors.As(err, &e) && e.Err == syscall.EINVAL {
|
||||||
|
return fmt.Errorf(`potentially insufficient UIDs or GIDs available in user namespace (requested %d:%d for %s): Check /etc/subuid and /etc/subgid if configured locally and run "podman system migrate": %w`, uid, gid, name, err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat contains file states that can be overridden with ContainersOverrideXattr.
|
||||||
|
type Stat struct {
|
||||||
|
IDs IDPair
|
||||||
|
Mode os.FileMode
|
||||||
|
Major int
|
||||||
|
Minor int
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatContainersOverrideXattr will format the given uid, gid, and mode into a string
|
||||||
|
// that can be used as the value for the ContainersOverrideXattr xattr.
|
||||||
|
func FormatContainersOverrideXattr(uid, gid, mode int) string {
|
||||||
|
return FormatContainersOverrideXattrDevice(uid, gid, fs.FileMode(mode), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatContainersOverrideXattrDevice will format the given uid, gid, and mode into a string
|
||||||
|
// that can be used as the value for the ContainersOverrideXattr xattr. For devices, it also
|
||||||
|
// needs the major and minor numbers.
|
||||||
|
func FormatContainersOverrideXattrDevice(uid, gid int, mode fs.FileMode, major, minor int) string {
|
||||||
|
typ := ""
|
||||||
|
switch mode & os.ModeType {
|
||||||
|
case os.ModeDir:
|
||||||
|
typ = "dir"
|
||||||
|
case os.ModeSymlink:
|
||||||
|
typ = "symlink"
|
||||||
|
case os.ModeNamedPipe:
|
||||||
|
typ = "pipe"
|
||||||
|
case os.ModeSocket:
|
||||||
|
typ = "socket"
|
||||||
|
case os.ModeDevice:
|
||||||
|
typ = fmt.Sprintf("block-%d-%d", major, minor)
|
||||||
|
case os.ModeDevice | os.ModeCharDevice:
|
||||||
|
typ = fmt.Sprintf("char-%d-%d", major, minor)
|
||||||
|
default:
|
||||||
|
typ = "file"
|
||||||
|
}
|
||||||
|
unixMode := mode & os.ModePerm
|
||||||
|
if mode&os.ModeSetuid != 0 {
|
||||||
|
unixMode |= 0o4000
|
||||||
|
}
|
||||||
|
if mode&os.ModeSetgid != 0 {
|
||||||
|
unixMode |= 0o2000
|
||||||
|
}
|
||||||
|
if mode&os.ModeSticky != 0 {
|
||||||
|
unixMode |= 0o1000
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d:%d:%04o:%s", uid, gid, unixMode, typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainersOverrideXattr will get and decode ContainersOverrideXattr.
|
||||||
|
func GetContainersOverrideXattr(path string) (Stat, error) {
|
||||||
|
xstat, err := system.Lgetxattr(path, ContainersOverrideXattr)
|
||||||
|
if err != nil {
|
||||||
|
return Stat{}, err
|
||||||
|
}
|
||||||
|
return parseOverrideXattr(xstat) // This will fail if (xstat, err) == (nil, nil), i.e. the xattr does not exist.
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOverrideXattr(xstat []byte) (Stat, error) {
|
||||||
|
var stat Stat
|
||||||
|
attrs := strings.Split(string(xstat), ":")
|
||||||
|
if len(attrs) < 3 {
|
||||||
|
return stat, fmt.Errorf("the number of parts in %s is less than 3",
|
||||||
|
ContainersOverrideXattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.ParseUint(attrs[0], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return stat, fmt.Errorf("failed to parse UID: %w", err)
|
||||||
|
}
|
||||||
|
stat.IDs.UID = int(value)
|
||||||
|
|
||||||
|
value, err = strconv.ParseUint(attrs[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return stat, fmt.Errorf("failed to parse GID: %w", err)
|
||||||
|
}
|
||||||
|
stat.IDs.GID = int(value)
|
||||||
|
|
||||||
|
value, err = strconv.ParseUint(attrs[2], 8, 32)
|
||||||
|
if err != nil {
|
||||||
|
return stat, fmt.Errorf("failed to parse mode: %w", err)
|
||||||
|
}
|
||||||
|
stat.Mode = os.FileMode(value) & os.ModePerm
|
||||||
|
if value&0o1000 != 0 {
|
||||||
|
stat.Mode |= os.ModeSticky
|
||||||
|
}
|
||||||
|
if value&0o2000 != 0 {
|
||||||
|
stat.Mode |= os.ModeSetgid
|
||||||
|
}
|
||||||
|
if value&0o4000 != 0 {
|
||||||
|
stat.Mode |= os.ModeSetuid
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(attrs) > 3 {
|
||||||
|
typ := attrs[3]
|
||||||
|
if strings.HasPrefix(typ, "file") {
|
||||||
|
} else if strings.HasPrefix(typ, "dir") {
|
||||||
|
stat.Mode |= os.ModeDir
|
||||||
|
} else if strings.HasPrefix(typ, "symlink") {
|
||||||
|
stat.Mode |= os.ModeSymlink
|
||||||
|
} else if strings.HasPrefix(typ, "pipe") {
|
||||||
|
stat.Mode |= os.ModeNamedPipe
|
||||||
|
} else if strings.HasPrefix(typ, "socket") {
|
||||||
|
stat.Mode |= os.ModeSocket
|
||||||
|
} else if strings.HasPrefix(typ, "block") {
|
||||||
|
stat.Mode |= os.ModeDevice
|
||||||
|
stat.Major, stat.Minor, err = parseDevice(typ)
|
||||||
|
if err != nil {
|
||||||
|
return stat, err
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(typ, "char") {
|
||||||
|
stat.Mode |= os.ModeDevice | os.ModeCharDevice
|
||||||
|
stat.Major, stat.Minor, err = parseDevice(typ)
|
||||||
|
if err != nil {
|
||||||
|
return stat, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return stat, fmt.Errorf("invalid file type %s", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDevice(typ string) (int, int, error) {
|
||||||
|
parts := strings.Split(typ, "-")
|
||||||
|
// If there are more than 3 parts, just ignore them to be forward compatible
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return 0, 0, fmt.Errorf("invalid device type %s", typ)
|
||||||
|
}
|
||||||
|
if parts[0] != "block" && parts[0] != "char" {
|
||||||
|
return 0, 0, fmt.Errorf("invalid device type %s", typ)
|
||||||
|
}
|
||||||
|
major, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, fmt.Errorf("failed to parse major number: %w", err)
|
||||||
|
}
|
||||||
|
minor, err := strconv.Atoi(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, fmt.Errorf("failed to parse minor number: %w", err)
|
||||||
|
}
|
||||||
|
return major, minor, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetContainersOverrideXattr will encode and set ContainersOverrideXattr.
|
||||||
|
func SetContainersOverrideXattr(path string, stat Stat) error {
|
||||||
|
value := FormatContainersOverrideXattrDevice(stat.IDs.UID, stat.IDs.GID, stat.Mode, stat.Major, stat.Minor)
|
||||||
|
return system.Lsetxattr(path, ContainersOverrideXattr, []byte(value), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SafeChown(name string, uid, gid int) error {
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
stat := Stat{
|
||||||
|
Mode: os.FileMode(0o0700),
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
gid = os.Getgid()
|
||||||
|
}
|
||||||
|
if stat, statErr := system.Stat(name); statErr == nil {
|
||||||
|
if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkChownErr(os.Chown(name, uid, gid), name, uid, gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SafeLchown(name string, uid, gid int) error {
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
stat := Stat{
|
||||||
|
Mode: os.FileMode(0o0700),
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
gid = os.Getgid()
|
||||||
|
}
|
||||||
|
if stat, statErr := system.Lstat(name); statErr == nil {
|
||||||
|
if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkChownErr(os.Lchown(name, uid, gid), name, uid, gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortByHostID []IDMap
|
||||||
|
|
||||||
|
func (e sortByHostID) Len() int { return len(e) }
|
||||||
|
func (e sortByHostID) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|
||||||
|
func (e sortByHostID) Less(i, j int) bool { return e[i].HostID < e[j].HostID }
|
||||||
|
|
||||||
|
type sortByContainerID []IDMap
|
||||||
|
|
||||||
|
func (e sortByContainerID) Len() int { return len(e) }
|
||||||
|
func (e sortByContainerID) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|
||||||
|
func (e sortByContainerID) Less(i, j int) bool { return e[i].ContainerID < e[j].ContainerID }
|
||||||
|
|
||||||
|
// IsContiguous checks if the specified mapping is contiguous and doesn't
|
||||||
|
// have any hole.
|
||||||
|
func IsContiguous(mappings []IDMap) bool {
|
||||||
|
if len(mappings) < 2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var mh sortByHostID = mappings[:]
|
||||||
|
sort.Sort(mh)
|
||||||
|
for i := 1; i < len(mh); i++ {
|
||||||
|
if mh[i].HostID != mh[i-1].HostID+mh[i-1].Size {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mc sortByContainerID = mappings[:]
|
||||||
|
sort.Sort(mc)
|
||||||
|
for i := 1; i < len(mc); i++ {
|
||||||
|
if mc[i].ContainerID != mc[i-1].ContainerID+mc[i-1].Size {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
91
vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go
generated
vendored
Normal file
91
vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
//go:build linux && cgo && libsubid
|
||||||
|
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/user"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -l subid
|
||||||
|
#include <shadow/subid.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct subid_range get_range(struct subid_range *ranges, int 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
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
var onceInit sync.Once
|
||||||
|
|
||||||
|
func readSubid(username string, isUser bool) (ranges, error) {
|
||||||
|
var ret ranges
|
||||||
|
uidstr := ""
|
||||||
|
|
||||||
|
if username == "ALL" {
|
||||||
|
return nil, errors.New("username ALL not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, err := user.Lookup(username); err == nil {
|
||||||
|
uidstr = u.Uid
|
||||||
|
}
|
||||||
|
|
||||||
|
onceInit.Do(func() {
|
||||||
|
C.subid_init(C.CString("storage"), C.stderr)
|
||||||
|
})
|
||||||
|
|
||||||
|
cUsername := C.CString(username)
|
||||||
|
defer C.free(unsafe.Pointer(cUsername))
|
||||||
|
|
||||||
|
cuidstr := C.CString(uidstr)
|
||||||
|
defer C.free(unsafe.Pointer(cuidstr))
|
||||||
|
|
||||||
|
var nRanges C.int
|
||||||
|
var cRanges *C.struct_subid_range
|
||||||
|
if isUser {
|
||||||
|
nRanges = C.subid_get_uid_ranges(cUsername, &cRanges)
|
||||||
|
if nRanges <= 0 {
|
||||||
|
nRanges = C.subid_get_uid_ranges(cuidstr, &cRanges)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nRanges = C.subid_get_gid_ranges(cUsername, &cRanges)
|
||||||
|
if nRanges <= 0 {
|
||||||
|
nRanges = C.subid_get_gid_ranges(cuidstr, &cRanges)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nRanges < 0 {
|
||||||
|
return nil, errors.New("cannot read subids")
|
||||||
|
}
|
||||||
|
defer C.free(unsafe.Pointer(cRanges))
|
||||||
|
|
||||||
|
for i := 0; i < int(nRanges); i++ {
|
||||||
|
r := C.get_range(cRanges, C.int(i))
|
||||||
|
newRange := subIDRange{
|
||||||
|
Start: int(r.start),
|
||||||
|
Length: int(r.count),
|
||||||
|
}
|
||||||
|
ret = append(ret, newRange)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSubuid(username string) (ranges, error) {
|
||||||
|
return readSubid(username, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSubgid(username string) (ranges, error) {
|
||||||
|
return readSubid(username, false)
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/fileutils"
|
||||||
|
"github.com/containers/storage/pkg/system"
|
||||||
|
"github.com/moby/sys/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
entOnce sync.Once
|
||||||
|
getentCmd string
|
||||||
|
)
|
||||||
|
|
||||||
|
func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
|
||||||
|
// make an array containing the original path asked for, plus (for mkAll == true)
|
||||||
|
// all path components leading up to the complete path that don't exist before we MkdirAll
|
||||||
|
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
|
||||||
|
// chown the full directory path if it exists
|
||||||
|
var paths []string
|
||||||
|
st, err := os.Stat(path)
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
paths = []string{path}
|
||||||
|
} else if err == nil {
|
||||||
|
if !st.IsDir() {
|
||||||
|
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
|
||||||
|
}
|
||||||
|
if chownExisting {
|
||||||
|
// short-circuit--we were called with an existing directory and chown was requested
|
||||||
|
return SafeChown(path, ownerUID, ownerGID)
|
||||||
|
}
|
||||||
|
// nothing to do; directory exists and chown was NOT requested
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if mkAll {
|
||||||
|
// walk back to "/" looking for directories which do not exist
|
||||||
|
// and add them to the paths array for chown after creation
|
||||||
|
dirPath := path
|
||||||
|
if !filepath.IsAbs(dirPath) {
|
||||||
|
return fmt.Errorf("path: %s should be absolute", dirPath)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
dirPath = filepath.Dir(dirPath)
|
||||||
|
if dirPath == "/" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err := fileutils.Exists(dirPath); err != nil && os.IsNotExist(err) {
|
||||||
|
paths = append(paths, dirPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(path, mode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := os.Mkdir(path, mode); err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// even if it existed, we will chown the requested path + any subpaths that
|
||||||
|
// didn't exist when we called MkdirAll
|
||||||
|
for _, pathComponent := range paths {
|
||||||
|
if err := SafeChown(pathComponent, ownerUID, ownerGID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
||||||
|
// if that uid, gid pair has access (execute bit) to the directory
|
||||||
|
func CanAccess(path string, pair IDPair) bool {
|
||||||
|
statInfo, err := system.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fileMode := os.FileMode(statInfo.Mode())
|
||||||
|
permBits := fileMode.Perm()
|
||||||
|
return accessible(statInfo.UID() == uint32(pair.UID),
|
||||||
|
statInfo.GID() == uint32(pair.GID), permBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessible(isOwner, isGroup bool, perms os.FileMode) bool {
|
||||||
|
if isOwner && (perms&0o100 == 0o100) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if isGroup && (perms&0o010 == 0o010) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if perms&0o001 == 0o001 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username,
|
||||||
|
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
|
||||||
|
func LookupUser(username string) (user.User, error) {
|
||||||
|
// first try a local system files lookup using existing capabilities
|
||||||
|
usr, err := user.LookupUser(username)
|
||||||
|
if err == nil {
|
||||||
|
return usr, nil
|
||||||
|
}
|
||||||
|
// local files lookup failed; attempt to call `getent` to query configured passwd dbs
|
||||||
|
usr, err = getentUser(fmt.Sprintf("%s %s", "passwd", username))
|
||||||
|
if err != nil {
|
||||||
|
return user.User{}, err
|
||||||
|
}
|
||||||
|
return usr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid,
|
||||||
|
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
|
||||||
|
func LookupUID(uid int) (user.User, error) {
|
||||||
|
// first try a local system files lookup using existing capabilities
|
||||||
|
usr, err := user.LookupUid(uid)
|
||||||
|
if err == nil {
|
||||||
|
return usr, nil
|
||||||
|
}
|
||||||
|
// local files lookup failed; attempt to call `getent` to query configured passwd dbs
|
||||||
|
return getentUser(fmt.Sprintf("%s %d", "passwd", uid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getentUser(args string) (user.User, error) {
|
||||||
|
reader, err := callGetent(args)
|
||||||
|
if err != nil {
|
||||||
|
return user.User{}, err
|
||||||
|
}
|
||||||
|
users, err := user.ParsePasswd(reader)
|
||||||
|
if err != nil {
|
||||||
|
return user.User{}, err
|
||||||
|
}
|
||||||
|
if len(users) == 0 {
|
||||||
|
return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", strings.Split(args, " ")[1])
|
||||||
|
}
|
||||||
|
return users[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name,
|
||||||
|
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
|
||||||
|
func LookupGroup(groupname string) (user.Group, error) {
|
||||||
|
// first try a local system files lookup using existing capabilities
|
||||||
|
group, err := user.LookupGroup(groupname)
|
||||||
|
if err == nil {
|
||||||
|
return group, nil
|
||||||
|
}
|
||||||
|
// local files lookup failed; attempt to call `getent` to query configured group dbs
|
||||||
|
return getentGroup(fmt.Sprintf("%s %s", "group", groupname))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID,
|
||||||
|
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
|
||||||
|
func LookupGID(gid int) (user.Group, error) {
|
||||||
|
// first try a local system files lookup using existing capabilities
|
||||||
|
group, err := user.LookupGid(gid)
|
||||||
|
if err == nil {
|
||||||
|
return group, nil
|
||||||
|
}
|
||||||
|
// local files lookup failed; attempt to call `getent` to query configured group dbs
|
||||||
|
return getentGroup(fmt.Sprintf("%s %d", "group", gid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getentGroup(args string) (user.Group, error) {
|
||||||
|
reader, err := callGetent(args)
|
||||||
|
if err != nil {
|
||||||
|
return user.Group{}, err
|
||||||
|
}
|
||||||
|
groups, err := user.ParseGroup(reader)
|
||||||
|
if err != nil {
|
||||||
|
return user.Group{}, err
|
||||||
|
}
|
||||||
|
if len(groups) == 0 {
|
||||||
|
return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", strings.Split(args, " ")[1])
|
||||||
|
}
|
||||||
|
return groups[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func callGetent(args string) (io.Reader, error) {
|
||||||
|
entOnce.Do(func() { getentCmd, _ = resolveBinary("getent") })
|
||||||
|
// if no `getent` command on host, can't do anything else
|
||||||
|
if getentCmd == "" {
|
||||||
|
return nil, fmt.Errorf("")
|
||||||
|
}
|
||||||
|
out, err := execCmd(getentCmd, args)
|
||||||
|
if err != nil {
|
||||||
|
exitCode, errC := system.GetExitCode(err)
|
||||||
|
if errC != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch exitCode {
|
||||||
|
case 1:
|
||||||
|
return nil, fmt.Errorf("getent reported invalid parameters/database unknown")
|
||||||
|
case 2:
|
||||||
|
terms := strings.Split(args, " ")
|
||||||
|
return nil, fmt.Errorf("getent unable to find entry %q in %s database", terms[1], terms[0])
|
||||||
|
case 3:
|
||||||
|
return nil, fmt.Errorf("getent database doesn't support enumeration")
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return bytes.NewReader(out), nil
|
||||||
|
}
|
11
vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go
generated
vendored
Normal file
11
vendor/github.com/containers/storage/pkg/idtools/idtools_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
//go:build !linux || !libsubid || !cgo
|
||||||
|
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
func readSubuid(username string) (ranges, error) {
|
||||||
|
return parseSubidFile(subuidFileName, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSubgid(username string) (ranges, error) {
|
||||||
|
return parseSubidFile(subgidFileName, username)
|
||||||
|
}
|
23
vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go
generated
vendored
Normal file
23
vendor/github.com/containers/storage/pkg/idtools/idtools_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Platforms such as Windows do not support the UID/GID concept. So make this
|
||||||
|
// just a wrapper around system.MkdirAll.
|
||||||
|
func mkdirAs(path string, mode os.FileMode, ownerUID, ownerGID int, mkAll, chownExisting bool) error {
|
||||||
|
if err := os.MkdirAll(path, mode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
||||||
|
// if that uid, gid pair has access (execute bit) to the directory
|
||||||
|
// Windows does not require/support this function, so always return true
|
||||||
|
func CanAccess(path string, pair IDPair) bool {
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/bits"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseTriple(spec []string) (container, host, size uint32, err error) {
|
||||||
|
cid, err := strconv.ParseUint(spec[0], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("parsing id map value %q: %w", spec[0], err)
|
||||||
|
}
|
||||||
|
hid, err := strconv.ParseUint(spec[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("parsing id map value %q: %w", spec[1], err)
|
||||||
|
}
|
||||||
|
sz, err := strconv.ParseUint(spec[2], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, fmt.Errorf("parsing id map value %q: %w", spec[2], err)
|
||||||
|
}
|
||||||
|
return uint32(cid), uint32(hid), uint32(sz), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseIDMap parses idmap triples from string.
|
||||||
|
func ParseIDMap(mapSpec []string, mapSetting string) (idmap []IDMap, err error) {
|
||||||
|
stdErr := fmt.Errorf("initializing ID mappings: %s setting is malformed expected [\"uint32:uint32:uint32\"]: %q", mapSetting, mapSpec)
|
||||||
|
for _, idMapSpec := range mapSpec {
|
||||||
|
if idMapSpec == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
idSpec := strings.Split(idMapSpec, ":")
|
||||||
|
if len(idSpec)%3 != 0 {
|
||||||
|
return nil, stdErr
|
||||||
|
}
|
||||||
|
for i := range idSpec {
|
||||||
|
if i%3 != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cid, hid, size, err := parseTriple(idSpec[i : i+3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, stdErr
|
||||||
|
}
|
||||||
|
// Avoid possible integer overflow on 32bit builds
|
||||||
|
if bits.UintSize == 32 && (cid > math.MaxInt32 || hid > math.MaxInt32 || size > math.MaxInt32) {
|
||||||
|
return nil, stdErr
|
||||||
|
}
|
||||||
|
mapping := IDMap{
|
||||||
|
ContainerID: int(cid),
|
||||||
|
HostID: int(hid),
|
||||||
|
Size: int(size),
|
||||||
|
}
|
||||||
|
idmap = append(idmap, mapping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idmap, nil
|
||||||
|
}
|
164
vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go
generated
vendored
Normal file
164
vendor/github.com/containers/storage/pkg/idtools/usergroupadd_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// add a user and/or group to Linux /etc/passwd, /etc/group using standard
|
||||||
|
// Linux distribution commands:
|
||||||
|
// adduser --system --shell /bin/false --disabled-login --disabled-password --no-create-home --group <username>
|
||||||
|
// useradd -r -s /bin/false <username>
|
||||||
|
|
||||||
|
var (
|
||||||
|
once sync.Once
|
||||||
|
userCommand string
|
||||||
|
|
||||||
|
cmdTemplates = map[string]string{
|
||||||
|
"adduser": "--system --shell /bin/false --no-create-home --disabled-login --disabled-password --group %s",
|
||||||
|
"useradd": "-r -s /bin/false %s",
|
||||||
|
"usermod": "-%s %d-%d %s",
|
||||||
|
}
|
||||||
|
|
||||||
|
idOutRegexp = regexp.Delayed(`uid=([0-9]+).*gid=([0-9]+)`)
|
||||||
|
// default length for a UID/GID subordinate range
|
||||||
|
defaultRangeLen = 65536
|
||||||
|
defaultRangeStart = 100000
|
||||||
|
userMod = "usermod"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddNamespaceRangesUser takes a username and uses the standard system
|
||||||
|
// utility to create a system user/group pair used to hold the
|
||||||
|
// /etc/sub{uid,gid} ranges which will be used for user namespace
|
||||||
|
// mapping ranges in containers.
|
||||||
|
func AddNamespaceRangesUser(name string) (int, int, error) {
|
||||||
|
if err := addUser(name); err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("adding user %q: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the system for the created uid and gid pair
|
||||||
|
out, err := execCmd("id", name)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("trying to find uid/gid for new user %q: %w", name, err)
|
||||||
|
}
|
||||||
|
matches := idOutRegexp.FindStringSubmatch(strings.TrimSpace(string(out)))
|
||||||
|
if len(matches) != 3 {
|
||||||
|
return -1, -1, fmt.Errorf("can't find uid, gid from `id` output: %q", string(out))
|
||||||
|
}
|
||||||
|
uid, err := strconv.Atoi(matches[1])
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("can't convert found uid (%s) to int: %w", matches[1], err)
|
||||||
|
}
|
||||||
|
gid, err := strconv.Atoi(matches[2])
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("can't convert found gid (%s) to int: %w", matches[2], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we need to create the subuid/subgid ranges for our new user/group (system users
|
||||||
|
// do not get auto-created ranges in subuid/subgid)
|
||||||
|
|
||||||
|
if err := createSubordinateRanges(name); err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("couldn't create subordinate ID ranges: %w", err)
|
||||||
|
}
|
||||||
|
return uid, gid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addUser(userName string) error {
|
||||||
|
once.Do(func() {
|
||||||
|
// set up which commands are used for adding users/groups dependent on distro
|
||||||
|
if _, err := resolveBinary("adduser"); err == nil {
|
||||||
|
userCommand = "adduser"
|
||||||
|
} else if _, err := resolveBinary("useradd"); err == nil {
|
||||||
|
userCommand = "useradd"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if userCommand == "" {
|
||||||
|
return fmt.Errorf("cannot add user; no useradd/adduser binary found")
|
||||||
|
}
|
||||||
|
args := fmt.Sprintf(cmdTemplates[userCommand], userName)
|
||||||
|
out, err := execCmd(userCommand, args)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add user with error: %w; output: %q", err, string(out))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSubordinateRanges(name string) error {
|
||||||
|
// first, we should verify that ranges weren't automatically created
|
||||||
|
// by the distro tooling
|
||||||
|
ranges, err := readSubuid(name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("while looking for subuid ranges for user %q: %w", name, err)
|
||||||
|
}
|
||||||
|
if len(ranges) == 0 {
|
||||||
|
// no UID ranges; let's create one
|
||||||
|
startID, err := findNextUIDRange()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't find available subuid range: %w", err)
|
||||||
|
}
|
||||||
|
out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "v", startID, startID+defaultRangeLen-1, name))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to add subuid range to user: %q; output: %s, err: %w", name, out, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges, err = readSubgid(name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("while looking for subgid ranges for user %q: %w", name, err)
|
||||||
|
}
|
||||||
|
if len(ranges) == 0 {
|
||||||
|
// no GID ranges; let's create one
|
||||||
|
startID, err := findNextGIDRange()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't find available subgid range: %w", err)
|
||||||
|
}
|
||||||
|
out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "w", startID, startID+defaultRangeLen-1, name))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to add subgid range to user: %q; output: %s, err: %w", name, out, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNextUIDRange() (int, error) {
|
||||||
|
ranges, err := readSubuid("ALL")
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subuid file: %w", err)
|
||||||
|
}
|
||||||
|
sort.Sort(ranges)
|
||||||
|
return findNextRangeStart(ranges)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNextGIDRange() (int, error) {
|
||||||
|
ranges, err := readSubgid("ALL")
|
||||||
|
if err != nil {
|
||||||
|
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subgid file: %w", err)
|
||||||
|
}
|
||||||
|
sort.Sort(ranges)
|
||||||
|
return findNextRangeStart(ranges)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNextRangeStart(rangeList ranges) (int, error) {
|
||||||
|
startID := defaultRangeStart
|
||||||
|
for _, arange := range rangeList {
|
||||||
|
if wouldOverlap(arange, startID) {
|
||||||
|
startID = arange.Start + arange.Length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return startID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wouldOverlap(arange subIDRange, ID int) bool {
|
||||||
|
low := ID
|
||||||
|
high := ID + defaultRangeLen
|
||||||
|
if (low >= arange.Start && low <= arange.Start+arange.Length) ||
|
||||||
|
(high <= arange.Start+arange.Length && high >= arange.Start) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
12
vendor/github.com/containers/storage/pkg/idtools/usergroupadd_unsupported.go
generated
vendored
Normal file
12
vendor/github.com/containers/storage/pkg/idtools/usergroupadd_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// AddNamespaceRangesUser takes a name and finds an unused uid, gid pair
|
||||||
|
// and calls the appropriate helper function to add the group and then
|
||||||
|
// the user to the group in /etc/group and /etc/passwd respectively.
|
||||||
|
func AddNamespaceRangesUser(name string) (int, int, error) {
|
||||||
|
return -1, -1, fmt.Errorf("no support for adding users or groups on this OS")
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package idtools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resolveBinary(binname string) (string, error) {
|
||||||
|
binaryPath, err := exec.LookPath(binname)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resolvedPath, err := filepath.EvalSymlinks(binaryPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// only return no error if the final resolved binary basename
|
||||||
|
// matches what was searched for
|
||||||
|
if filepath.Base(resolvedPath) == binname {
|
||||||
|
return resolvedPath, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execCmd(cmd, args string) ([]byte, error) {
|
||||||
|
execCmd := exec.Command(cmd, strings.Split(args, " ")...)
|
||||||
|
return execCmd.CombinedOutput()
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var flags = map[string]struct {
|
||||||
|
clear bool
|
||||||
|
flag int
|
||||||
|
}{
|
||||||
|
"defaults": {false, 0},
|
||||||
|
"ro": {false, RDONLY},
|
||||||
|
"rw": {true, RDONLY},
|
||||||
|
"suid": {true, NOSUID},
|
||||||
|
"nosuid": {false, NOSUID},
|
||||||
|
"dev": {true, NODEV},
|
||||||
|
"nodev": {false, NODEV},
|
||||||
|
"exec": {true, NOEXEC},
|
||||||
|
"noexec": {false, NOEXEC},
|
||||||
|
"sync": {false, SYNCHRONOUS},
|
||||||
|
"async": {true, SYNCHRONOUS},
|
||||||
|
"dirsync": {false, DIRSYNC},
|
||||||
|
"remount": {false, REMOUNT},
|
||||||
|
"mand": {false, MANDLOCK},
|
||||||
|
"nomand": {true, MANDLOCK},
|
||||||
|
"atime": {true, NOATIME},
|
||||||
|
"noatime": {false, NOATIME},
|
||||||
|
"diratime": {true, NODIRATIME},
|
||||||
|
"nodiratime": {false, NODIRATIME},
|
||||||
|
"bind": {false, BIND},
|
||||||
|
"rbind": {false, RBIND},
|
||||||
|
"unbindable": {false, UNBINDABLE},
|
||||||
|
"runbindable": {false, RUNBINDABLE},
|
||||||
|
"private": {false, PRIVATE},
|
||||||
|
"rprivate": {false, RPRIVATE},
|
||||||
|
"shared": {false, SHARED},
|
||||||
|
"rshared": {false, RSHARED},
|
||||||
|
"slave": {false, SLAVE},
|
||||||
|
"rslave": {false, RSLAVE},
|
||||||
|
"relatime": {false, RELATIME},
|
||||||
|
"norelatime": {true, RELATIME},
|
||||||
|
"strictatime": {false, STRICTATIME},
|
||||||
|
"nostrictatime": {true, STRICTATIME},
|
||||||
|
}
|
||||||
|
|
||||||
|
var validFlags = map[string]bool{
|
||||||
|
"": true,
|
||||||
|
"size": true,
|
||||||
|
"mode": true,
|
||||||
|
"uid": true,
|
||||||
|
"gid": true,
|
||||||
|
"nr_inodes": true,
|
||||||
|
"nr_blocks": true,
|
||||||
|
"mpol": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var propagationFlags = map[string]bool{
|
||||||
|
"bind": true,
|
||||||
|
"rbind": true,
|
||||||
|
"unbindable": true,
|
||||||
|
"runbindable": true,
|
||||||
|
"private": true,
|
||||||
|
"rprivate": true,
|
||||||
|
"shared": true,
|
||||||
|
"rshared": true,
|
||||||
|
"slave": true,
|
||||||
|
"rslave": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeTmpfsOptions merge mount options to make sure there is no duplicate.
|
||||||
|
func MergeTmpfsOptions(options []string) ([]string, error) {
|
||||||
|
// We use collisions maps to remove duplicates.
|
||||||
|
// For flag, the key is the flag value (the key for propagation flag is -1)
|
||||||
|
// For data=value, the key is the data
|
||||||
|
flagCollisions := map[int]bool{}
|
||||||
|
dataCollisions := map[string]bool{}
|
||||||
|
|
||||||
|
var newOptions []string
|
||||||
|
// We process in reverse order
|
||||||
|
for i := len(options) - 1; i >= 0; i-- {
|
||||||
|
option := options[i]
|
||||||
|
if option == "defaults" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if f, ok := flags[option]; ok && f.flag != 0 {
|
||||||
|
// There is only one propagation mode
|
||||||
|
key := f.flag
|
||||||
|
if propagationFlags[option] {
|
||||||
|
key = -1
|
||||||
|
}
|
||||||
|
// Check to see if there is collision for flag
|
||||||
|
if !flagCollisions[key] {
|
||||||
|
// We prepend the option and add to collision map
|
||||||
|
newOptions = append([]string{option}, newOptions...)
|
||||||
|
flagCollisions[key] = true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opt, _, ok := strings.Cut(option, "=")
|
||||||
|
if !ok || !validFlags[opt] {
|
||||||
|
return nil, fmt.Errorf("invalid tmpfs option %q", opt)
|
||||||
|
}
|
||||||
|
if !dataCollisions[opt] {
|
||||||
|
// We prepend the option and add to collision map
|
||||||
|
newOptions = append([]string{option}, newOptions...)
|
||||||
|
dataCollisions[opt] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newOptions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseOptions parses fstab type mount options into mount() flags
|
||||||
|
// and device specific data
|
||||||
|
func ParseOptions(options string) (int, string) {
|
||||||
|
var (
|
||||||
|
flag int
|
||||||
|
data []string
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, o := range strings.Split(options, ",") {
|
||||||
|
// If the option does not exist in the flags table or the flag
|
||||||
|
// is not supported on the platform,
|
||||||
|
// then it is a data value for a specific fs type
|
||||||
|
if f, exists := flags[o]; exists && f.flag != 0 {
|
||||||
|
if f.clear {
|
||||||
|
flag &= ^f.flag
|
||||||
|
} else {
|
||||||
|
flag |= f.flag
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = append(data, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag, strings.Join(data, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTmpfsOptions parse fstab type mount options into flags and data
|
||||||
|
func ParseTmpfsOptions(options string) (int, string, error) {
|
||||||
|
flags, data := ParseOptions(options)
|
||||||
|
for _, o := range strings.Split(data, ",") {
|
||||||
|
opt, _, _ := strings.Cut(o, "=")
|
||||||
|
if !validFlags[opt] {
|
||||||
|
return 0, "", fmt.Errorf("invalid tmpfs option %q", opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flags, data, nil
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RDONLY will mount the file system read-only.
|
||||||
|
RDONLY = unix.MNT_RDONLY
|
||||||
|
|
||||||
|
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||||
|
// take effect.
|
||||||
|
NOSUID = unix.MNT_NOSUID
|
||||||
|
|
||||||
|
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||||
|
NOEXEC = unix.MNT_NOEXEC
|
||||||
|
|
||||||
|
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
|
||||||
|
SYNCHRONOUS = unix.MNT_SYNCHRONOUS
|
||||||
|
|
||||||
|
// REMOUNT will attempt to remount an already-mounted file system. This is
|
||||||
|
// commonly used to change the mount flags for a file system, especially to
|
||||||
|
// make a readonly file system writeable. It does not change device or mount
|
||||||
|
// point.
|
||||||
|
REMOUNT = unix.MNT_UPDATE
|
||||||
|
|
||||||
|
// NOATIME will not update the file access time when reading from a file.
|
||||||
|
NOATIME = unix.MNT_NOATIME
|
||||||
|
|
||||||
|
mntDetach = unix.MNT_FORCE
|
||||||
|
|
||||||
|
NODIRATIME = 0
|
||||||
|
NODEV = 0
|
||||||
|
DIRSYNC = 0
|
||||||
|
MANDLOCK = 0
|
||||||
|
BIND = 0
|
||||||
|
RBIND = 0
|
||||||
|
UNBINDABLE = 0
|
||||||
|
RUNBINDABLE = 0
|
||||||
|
PRIVATE = 0
|
||||||
|
RPRIVATE = 0
|
||||||
|
SLAVE = 0
|
||||||
|
RSLAVE = 0
|
||||||
|
SHARED = 0
|
||||||
|
RSHARED = 0
|
||||||
|
RELATIME = 0
|
||||||
|
STRICTATIME = 0
|
||||||
|
)
|
|
@ -0,0 +1,87 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RDONLY will mount the file system read-only.
|
||||||
|
RDONLY = unix.MS_RDONLY
|
||||||
|
|
||||||
|
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||||
|
// take effect.
|
||||||
|
NOSUID = unix.MS_NOSUID
|
||||||
|
|
||||||
|
// NODEV will not interpret character or block special devices on the file
|
||||||
|
// system.
|
||||||
|
NODEV = unix.MS_NODEV
|
||||||
|
|
||||||
|
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||||
|
NOEXEC = unix.MS_NOEXEC
|
||||||
|
|
||||||
|
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
|
||||||
|
SYNCHRONOUS = unix.MS_SYNCHRONOUS
|
||||||
|
|
||||||
|
// DIRSYNC will force all directory updates within the file system to be done
|
||||||
|
// synchronously. This affects the following system calls: create, link,
|
||||||
|
// unlink, symlink, mkdir, rmdir, mknod and rename.
|
||||||
|
DIRSYNC = unix.MS_DIRSYNC
|
||||||
|
|
||||||
|
// REMOUNT will attempt to remount an already-mounted file system. This is
|
||||||
|
// commonly used to change the mount flags for a file system, especially to
|
||||||
|
// make a readonly file system writeable. It does not change device or mount
|
||||||
|
// point.
|
||||||
|
REMOUNT = unix.MS_REMOUNT
|
||||||
|
|
||||||
|
// MANDLOCK will force mandatory locks on a filesystem.
|
||||||
|
MANDLOCK = unix.MS_MANDLOCK
|
||||||
|
|
||||||
|
// NOATIME will not update the file access time when reading from a file.
|
||||||
|
NOATIME = unix.MS_NOATIME
|
||||||
|
|
||||||
|
// NODIRATIME will not update the directory access time.
|
||||||
|
NODIRATIME = unix.MS_NODIRATIME
|
||||||
|
|
||||||
|
// BIND remounts a subtree somewhere else.
|
||||||
|
BIND = unix.MS_BIND
|
||||||
|
|
||||||
|
// RBIND remounts a subtree and all possible submounts somewhere else.
|
||||||
|
RBIND = unix.MS_BIND | unix.MS_REC
|
||||||
|
|
||||||
|
// UNBINDABLE creates a mount which cannot be cloned through a bind operation.
|
||||||
|
UNBINDABLE = unix.MS_UNBINDABLE
|
||||||
|
|
||||||
|
// RUNBINDABLE marks the entire mount tree as UNBINDABLE.
|
||||||
|
RUNBINDABLE = unix.MS_UNBINDABLE | unix.MS_REC
|
||||||
|
|
||||||
|
// PRIVATE creates a mount which carries no propagation abilities.
|
||||||
|
PRIVATE = unix.MS_PRIVATE
|
||||||
|
|
||||||
|
// RPRIVATE marks the entire mount tree as PRIVATE.
|
||||||
|
RPRIVATE = unix.MS_PRIVATE | unix.MS_REC
|
||||||
|
|
||||||
|
// SLAVE creates a mount which receives propagation from its master, but not
|
||||||
|
// vice versa.
|
||||||
|
SLAVE = unix.MS_SLAVE
|
||||||
|
|
||||||
|
// RSLAVE marks the entire mount tree as SLAVE.
|
||||||
|
RSLAVE = unix.MS_SLAVE | unix.MS_REC
|
||||||
|
|
||||||
|
// SHARED creates a mount which provides the ability to create mirrors of
|
||||||
|
// that mount such that mounts and unmounts within any of the mirrors
|
||||||
|
// propagate to the other mirrors.
|
||||||
|
SHARED = unix.MS_SHARED
|
||||||
|
|
||||||
|
// RSHARED marks the entire mount tree as SHARED.
|
||||||
|
RSHARED = unix.MS_SHARED | unix.MS_REC
|
||||||
|
|
||||||
|
// RELATIME updates inode access times relative to modify or change time.
|
||||||
|
RELATIME = unix.MS_RELATIME
|
||||||
|
|
||||||
|
// STRICTATIME allows to explicitly request full atime updates. This makes
|
||||||
|
// it possible for the kernel to default to relatime or noatime but still
|
||||||
|
// allow userspace to override it.
|
||||||
|
STRICTATIME = unix.MS_STRICTATIME
|
||||||
|
|
||||||
|
mntDetach = unix.MNT_DETACH
|
||||||
|
)
|
31
vendor/github.com/containers/storage/pkg/mount/flags_unsupported.go
generated
vendored
Normal file
31
vendor/github.com/containers/storage/pkg/mount/flags_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//go:build !linux && !freebsd
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
// These flags are unsupported.
|
||||||
|
const (
|
||||||
|
BIND = 0
|
||||||
|
DIRSYNC = 0
|
||||||
|
MANDLOCK = 0
|
||||||
|
NOATIME = 0
|
||||||
|
NODEV = 0
|
||||||
|
NODIRATIME = 0
|
||||||
|
NOEXEC = 0
|
||||||
|
NOSUID = 0
|
||||||
|
UNBINDABLE = 0
|
||||||
|
RUNBINDABLE = 0
|
||||||
|
PRIVATE = 0
|
||||||
|
RPRIVATE = 0
|
||||||
|
SHARED = 0
|
||||||
|
RSHARED = 0
|
||||||
|
SLAVE = 0
|
||||||
|
RSLAVE = 0
|
||||||
|
RBIND = 0
|
||||||
|
RELATIME = 0
|
||||||
|
RELATIVE = 0
|
||||||
|
REMOUNT = 0
|
||||||
|
STRICTATIME = 0
|
||||||
|
SYNCHRONOUS = 0
|
||||||
|
RDONLY = 0
|
||||||
|
mntDetach = 0
|
||||||
|
)
|
|
@ -0,0 +1,110 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mountError holds an error from a mount or unmount operation
|
||||||
|
type mountError struct {
|
||||||
|
op string
|
||||||
|
source, target string
|
||||||
|
flags uintptr
|
||||||
|
data string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a string representation of mountError
|
||||||
|
func (e *mountError) Error() string {
|
||||||
|
out := e.op + " "
|
||||||
|
|
||||||
|
if e.source != "" {
|
||||||
|
out += e.source + ":" + e.target
|
||||||
|
} else {
|
||||||
|
out += e.target
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.flags != uintptr(0) {
|
||||||
|
out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
|
||||||
|
}
|
||||||
|
if e.data != "" {
|
||||||
|
out += ", data: " + e.data
|
||||||
|
}
|
||||||
|
|
||||||
|
out += ": " + e.err.Error()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cause returns the underlying cause of the error
|
||||||
|
func (e *mountError) Cause() error {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the underlying cause of the error
|
||||||
|
func (e *mountError) Unwrap() error {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount will mount filesystem according to the specified configuration, on the
|
||||||
|
// condition that the target path is *not* already mounted. Options must be
|
||||||
|
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||||
|
// flags.go for supported option flags.
|
||||||
|
func Mount(device, target, mType, options string) error {
|
||||||
|
flag, data := ParseOptions(options)
|
||||||
|
if flag&REMOUNT != REMOUNT {
|
||||||
|
if mounted, err := Mounted(target); err != nil || mounted {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mount(device, target, mType, uintptr(flag), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceMount will mount a filesystem according to the specified configuration,
|
||||||
|
// *regardless* if the target path is not already mounted. Options must be
|
||||||
|
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||||
|
// flags.go for supported option flags.
|
||||||
|
func ForceMount(device, target, mType, options string) error {
|
||||||
|
flag, data := ParseOptions(options)
|
||||||
|
return mount(device, target, mType, uintptr(flag), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount lazily unmounts a filesystem on supported platforms, otherwise
|
||||||
|
// does a normal unmount.
|
||||||
|
func Unmount(target string) error {
|
||||||
|
return unmount(target, mntDetach)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecursiveUnmount unmounts the target and all mounts underneath, starting with
|
||||||
|
// the deepest mount first.
|
||||||
|
func RecursiveUnmount(target string) error {
|
||||||
|
mounts, err := GetMounts()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the deepest mount be first
|
||||||
|
sort.Slice(mounts, func(i, j int) bool {
|
||||||
|
return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint)
|
||||||
|
})
|
||||||
|
|
||||||
|
for i, m := range mounts {
|
||||||
|
if !strings.HasPrefix(m.Mountpoint, target) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := Unmount(m.Mountpoint); err != nil && i == len(mounts)-1 {
|
||||||
|
return err
|
||||||
|
// Ignore errors for submounts and continue trying to unmount others
|
||||||
|
// The final unmount should fail if there are any submounts remaining
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceUnmount lazily unmounts a filesystem on supported platforms,
|
||||||
|
// otherwise does a normal unmount.
|
||||||
|
//
|
||||||
|
// Deprecated: please use Unmount instead, it is identical.
|
||||||
|
func ForceUnmount(target string) error {
|
||||||
|
return unmount(target, mntDetach)
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
//go:build freebsd && cgo
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/_iovec.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func allocateIOVecs(options []string) []C.struct_iovec {
|
||||||
|
out := make([]C.struct_iovec, len(options))
|
||||||
|
for i, option := range options {
|
||||||
|
out[i].iov_base = unsafe.Pointer(C.CString(option))
|
||||||
|
out[i].iov_len = C.size_t(len(option) + 1)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||||
|
isNullFS := false
|
||||||
|
|
||||||
|
options := []string{"fspath", target}
|
||||||
|
|
||||||
|
if data != "" {
|
||||||
|
xs := strings.Split(data, ",")
|
||||||
|
for _, x := range xs {
|
||||||
|
if x == "bind" {
|
||||||
|
isNullFS = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, val, _ := strings.Cut(x, "=")
|
||||||
|
options = append(options, name)
|
||||||
|
options = append(options, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isNullFS {
|
||||||
|
options = append(options, "fstype", "nullfs", "target", device)
|
||||||
|
} else {
|
||||||
|
options = append(options, "fstype", mType, "from", device)
|
||||||
|
}
|
||||||
|
rawOptions := allocateIOVecs(options)
|
||||||
|
for _, rawOption := range rawOptions {
|
||||||
|
defer C.free(rawOption.iov_base)
|
||||||
|
}
|
||||||
|
|
||||||
|
if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 {
|
||||||
|
reason := C.GoString(C.strerror(*C.__error()))
|
||||||
|
return fmt.Errorf("failed to call nmount: %s", reason)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ptypes is the set propagation types.
|
||||||
|
ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE
|
||||||
|
|
||||||
|
// pflags is the full set valid flags for a change propagation call.
|
||||||
|
pflags = ptypes | unix.MS_REC | unix.MS_SILENT
|
||||||
|
|
||||||
|
// broflags is the combination of bind and read only
|
||||||
|
broflags = unix.MS_BIND | unix.MS_RDONLY
|
||||||
|
|
||||||
|
none = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isremount returns true if either device name or flags identify a remount request, false otherwise.
|
||||||
|
func isremount(device string, flags uintptr) bool {
|
||||||
|
switch {
|
||||||
|
// We treat device "" and "none" as a remount request to provide compatibility with
|
||||||
|
// requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts.
|
||||||
|
case flags&unix.MS_REMOUNT != 0, device == "", device == none:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mount(device, target, mType string, flags uintptr, data string) error {
|
||||||
|
oflags := flags &^ ptypes
|
||||||
|
if !isremount(device, flags) || data != "" {
|
||||||
|
// Initial call applying all non-propagation flags for mount
|
||||||
|
// or remount with changed data
|
||||||
|
if err := unix.Mount(device, target, mType, oflags, data); err != nil {
|
||||||
|
return &mountError{
|
||||||
|
op: "mount",
|
||||||
|
source: device,
|
||||||
|
target: target,
|
||||||
|
flags: oflags,
|
||||||
|
data: data,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags&ptypes != 0 {
|
||||||
|
// Change the propagation type.
|
||||||
|
if err := unix.Mount("", target, "", flags&pflags, ""); err != nil {
|
||||||
|
return &mountError{
|
||||||
|
op: "remount",
|
||||||
|
target: target,
|
||||||
|
flags: flags & pflags,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if oflags&broflags == broflags {
|
||||||
|
// Remount the bind to apply read only.
|
||||||
|
if err := unix.Mount("", target, "", oflags|unix.MS_REMOUNT, ""); err != nil {
|
||||||
|
return &mountError{
|
||||||
|
op: "remount-ro",
|
||||||
|
target: target,
|
||||||
|
flags: oflags | unix.MS_REMOUNT,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
7
vendor/github.com/containers/storage/pkg/mount/mounter_unsupported.go
generated
vendored
Normal file
7
vendor/github.com/containers/storage/pkg/mount/mounter_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//go:build !linux && !(freebsd && cgo)
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/moby/sys/mountinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Info = mountinfo.Info
|
||||||
|
|
||||||
|
var Mounted = mountinfo.Mounted
|
||||||
|
|
||||||
|
func GetMounts() ([]*Info, error) {
|
||||||
|
return mountinfo.GetMounts(nil)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"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)
|
||||||
|
}
|
64
vendor/github.com/containers/storage/pkg/mount/sharedsubtree_linux.go
generated
vendored
Normal file
64
vendor/github.com/containers/storage/pkg/mount/sharedsubtree_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeShared(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, SHARED)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeRShared(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, RSHARED)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakePrivate(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
|
||||||
|
// enabled. See the supported options in flags.go for further reference.
|
||||||
|
func MakeRPrivate(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, RPRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeSlave(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, SLAVE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
|
||||||
|
// See the supported options in flags.go for further reference.
|
||||||
|
func MakeRSlave(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, RSLAVE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
|
||||||
|
// enabled. See the supported options in flags.go for further reference.
|
||||||
|
func MakeUnbindable(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, UNBINDABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
|
||||||
|
// option enabled. See the supported options in flags.go for further reference.
|
||||||
|
func MakeRUnbindable(mountPoint string) error {
|
||||||
|
return ensureMountedAs(mountPoint, RUNBINDABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureMountedAs(mnt string, flags int) error {
|
||||||
|
mounted, err := Mounted(mnt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mounted {
|
||||||
|
if err := mount(mnt, mnt, "none", uintptr(BIND), ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mount("", mnt, "none", uintptr(flags), "")
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func unmount(target string, flags int) error {
|
||||||
|
var err error
|
||||||
|
for range 50 {
|
||||||
|
err = unix.Unmount(target, flags)
|
||||||
|
switch err {
|
||||||
|
case unix.EBUSY:
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
case unix.EINVAL, nil:
|
||||||
|
// Ignore "not mounted" error here. Note the same error
|
||||||
|
// can be returned if flags are invalid, so this code
|
||||||
|
// assumes that the flags value is always correct.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mountError{
|
||||||
|
op: "umount",
|
||||||
|
target: target,
|
||||||
|
flags: uintptr(flags),
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
7
vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go
generated
vendored
Normal file
7
vendor/github.com/containers/storage/pkg/mount/unmount_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
func unmount(target string, flag int) error {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
# reexec
|
||||||
|
|
||||||
|
The `reexec` package facilitates the busybox style reexec of the docker binary that we require because
|
||||||
|
of the forking limitations of using Go. Handlers can be registered with a name and the argv 0 of
|
||||||
|
the exec of the binary will be used to find and execute custom init paths.
|
37
vendor/github.com/containers/storage/pkg/reexec/command_freebsd.go
generated
vendored
Normal file
37
vendor/github.com/containers/storage/pkg/reexec/command_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//go:build freebsd
|
||||||
|
|
||||||
|
package reexec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Self returns the path to the current process's binary.
|
||||||
|
// Uses sysctl.
|
||||||
|
func Self() string {
|
||||||
|
path, err := unix.SysctlArgs("kern.proc.pathname", -1)
|
||||||
|
if err == nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return os.Args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns *exec.Cmd which has Path as current binary.
|
||||||
|
// For example if current binary is "docker" at "/usr/bin/", then cmd.Path will
|
||||||
|
// be set to "/usr/bin/docker".
|
||||||
|
func Command(args ...string) *exec.Cmd {
|
||||||
|
cmd := exec.Command(Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandContext returns *exec.Cmd which has Path as current binary.
|
||||||
|
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
|
||||||
|
cmd := exec.CommandContext(ctx, Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package reexec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Self returns the path to the current process's binary.
|
||||||
|
// Returns "/proc/self/exe".
|
||||||
|
func Self() string {
|
||||||
|
return "/proc/self/exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns *exec.Cmd which has Path as current binary.
|
||||||
|
// This will use the in-memory version (/proc/self/exe) of the current binary,
|
||||||
|
// it is thus safe to delete or replace the on-disk binary (os.Args[0]).
|
||||||
|
func Command(args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
cmd := exec.Command(Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandContext returns *exec.Cmd which has Path as current binary.
|
||||||
|
// This will use the in-memory version (/proc/self/exe) of the current binary,
|
||||||
|
// it is thus safe to delete or replace the on-disk binary (os.Args[0]).
|
||||||
|
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
cmd := exec.CommandContext(ctx, Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//go:build solaris || darwin
|
||||||
|
|
||||||
|
package reexec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Self returns the path to the current process's binary.
|
||||||
|
// Uses os.Args[0].
|
||||||
|
func Self() string {
|
||||||
|
return naiveSelf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns *exec.Cmd which has Path as current binary.
|
||||||
|
// For example if current binary is "docker" at "/usr/bin/", then cmd.Path will
|
||||||
|
// be set to "/usr/bin/docker".
|
||||||
|
func Command(args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
cmd := exec.Command(Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandContext returns *exec.Cmd which has Path as current binary.
|
||||||
|
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
cmd := exec.CommandContext(ctx, Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
20
vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go
generated
vendored
Normal file
20
vendor/github.com/containers/storage/pkg/reexec/command_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build !linux && !windows && !freebsd && !solaris && !darwin
|
||||||
|
|
||||||
|
package reexec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin.
|
||||||
|
func Command(args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandContext is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin.
|
||||||
|
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
return nil
|
||||||
|
}
|
34
vendor/github.com/containers/storage/pkg/reexec/command_windows.go
generated
vendored
Normal file
34
vendor/github.com/containers/storage/pkg/reexec/command_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package reexec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Self returns the path to the current process's binary.
|
||||||
|
// Uses os.Args[0].
|
||||||
|
func Self() string {
|
||||||
|
return naiveSelf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns *exec.Cmd which has Path as current binary.
|
||||||
|
// For example if current binary is "docker.exe" at "C:\", then cmd.Path will
|
||||||
|
// be set to "C:\docker.exe".
|
||||||
|
func Command(args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
cmd := exec.Command(Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns *exec.Cmd which has Path as current binary.
|
||||||
|
// For example if current binary is "docker.exe" at "C:\", then cmd.Path will
|
||||||
|
// be set to "C:\docker.exe".
|
||||||
|
func CommandContext(ctx context.Context, args ...string) *exec.Cmd {
|
||||||
|
panicIfNotInitialized()
|
||||||
|
cmd := exec.CommandContext(ctx, Self())
|
||||||
|
cmd.Args = args
|
||||||
|
return cmd
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package reexec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
registeredInitializers = make(map[string]func())
|
||||||
|
initWasCalled = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register adds an initialization func under the specified name
|
||||||
|
func Register(name string, initializer func()) {
|
||||||
|
if _, exists := registeredInitializers[name]; exists {
|
||||||
|
panic(fmt.Sprintf("reexec func already registered under name %q", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
registeredInitializers[name] = initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init is called as the first part of the exec process and returns true if an
|
||||||
|
// initialization function was called.
|
||||||
|
func Init() bool {
|
||||||
|
initializer, exists := registeredInitializers[os.Args[0]]
|
||||||
|
initWasCalled = true
|
||||||
|
if exists {
|
||||||
|
initializer()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func panicIfNotInitialized() {
|
||||||
|
if !initWasCalled {
|
||||||
|
// The reexec package is used to run subroutines in
|
||||||
|
// subprocesses which would otherwise have unacceptable side
|
||||||
|
// effects on the main thread. If you found this error, then
|
||||||
|
// your program uses a package which needs to do this. In
|
||||||
|
// order for that to work, main() should start with this
|
||||||
|
// boilerplate, or an equivalent:
|
||||||
|
// if reexec.Init() {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
panic("a library subroutine needed to run a subprocess, but reexec.Init() was not called in main()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func naiveSelf() string {
|
||||||
|
name := os.Args[0]
|
||||||
|
if filepath.Base(name) == name {
|
||||||
|
if lp, err := exec.LookPath(name); err == nil {
|
||||||
|
return lp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// handle conversion of relative paths to absolute
|
||||||
|
if absName, err := filepath.Abs(name); err == nil {
|
||||||
|
return absName
|
||||||
|
}
|
||||||
|
// if we couldn't get absolute name, return original
|
||||||
|
// (NOTE: Go only errors on Abs() if os.Getwd fails)
|
||||||
|
return name
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
package regexp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Regexp is a wrapper struct used for wrapping MustCompile regex expressions
|
||||||
|
// used as global variables. Using this structure helps speed the startup time
|
||||||
|
// of apps that want to use global regex variables. This library initializes them on
|
||||||
|
// first use as opposed to the start of the executable.
|
||||||
|
type Regexp struct {
|
||||||
|
*regexpStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
type regexpStruct struct {
|
||||||
|
_ noCopy
|
||||||
|
once sync.Once
|
||||||
|
regexp *regexp.Regexp
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Delayed(val string) Regexp {
|
||||||
|
re := ®expStruct{
|
||||||
|
val: val,
|
||||||
|
}
|
||||||
|
if precompile {
|
||||||
|
re.regexp = regexp.MustCompile(re.val)
|
||||||
|
}
|
||||||
|
return Regexp{re}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) compile() {
|
||||||
|
if precompile {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
re.once.Do(func() {
|
||||||
|
re.regexp = regexp.MustCompile(re.val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) Expand(dst []byte, template []byte, src []byte, match []int) []byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.Expand(dst, template, src, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) ExpandString(dst []byte, template string, src string, match []int) []byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.ExpandString(dst, template, src, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) Find(b []byte) []byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.Find(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAll(b []byte, n int) [][]byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAll(b, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAllIndex(b []byte, n int) [][]int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAllIndex(b, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAllString(s string, n int) []string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAllString(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAllStringIndex(s string, n int) [][]int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAllStringIndex(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAllStringSubmatch(s string, n int) [][]string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAllStringSubmatch(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAllStringSubmatchIndex(s string, n int) [][]int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAllStringSubmatchIndex(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAllSubmatch(b []byte, n int) [][][]byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAllSubmatch(b, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindAllSubmatchIndex(b []byte, n int) [][]int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindAllSubmatchIndex(b, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindIndex(b []byte) (loc []int) {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindIndex(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindReaderIndex(r io.RuneReader) (loc []int) {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindReaderIndex(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindReaderSubmatchIndex(r io.RuneReader) []int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindReaderSubmatchIndex(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindString(s string) string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindStringIndex(s string) (loc []int) {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindStringIndex(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindStringSubmatch(s string) []string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindStringSubmatch(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindStringSubmatchIndex(s string) []int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindStringSubmatchIndex(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindSubmatch(b []byte) [][]byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindSubmatch(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) FindSubmatchIndex(b []byte) []int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.FindSubmatchIndex(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) LiteralPrefix() (prefix string, complete bool) {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.LiteralPrefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) Longest() {
|
||||||
|
re.compile()
|
||||||
|
re.regexp.Longest()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) Match(b []byte) bool {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.Match(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) MatchReader(r io.RuneReader) bool {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.MatchReader(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) MatchString(s string) bool {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.MatchString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) NumSubexp() int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.NumSubexp()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) ReplaceAll(src, repl []byte) []byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.ReplaceAll(src, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.ReplaceAllFunc(src, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) ReplaceAllLiteral(src, repl []byte) []byte {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.ReplaceAllLiteral(src, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) ReplaceAllLiteralString(src, repl string) string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.ReplaceAllLiteralString(src, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) ReplaceAllString(src, repl string) string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.ReplaceAllString(src, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) ReplaceAllStringFunc(src string, repl func(string) string) string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.ReplaceAllStringFunc(src, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) Split(s string, n int) []string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.Split(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) String() string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) SubexpIndex(name string) int {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.SubexpIndex(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *regexpStruct) SubexpNames() []string {
|
||||||
|
re.compile()
|
||||||
|
return re.regexp.SubexpNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
// noCopy may be added to structs which must not be copied
|
||||||
|
// after the first use.
|
||||||
|
//
|
||||||
|
// See https://golang.org/issues/8005#issuecomment-190753527
|
||||||
|
// for details.
|
||||||
|
//
|
||||||
|
// Note that it must not be embedded, due to the Lock and Unlock methods.
|
||||||
|
type noCopy struct{}
|
||||||
|
|
||||||
|
// Lock is a no-op used by -copylocks checker from `go vet`.
|
||||||
|
func (*noCopy) Lock() {}
|
||||||
|
func (*noCopy) Unlock() {}
|
5
vendor/github.com/containers/storage/pkg/regexp/regexp_dontprecompile.go
generated
vendored
Normal file
5
vendor/github.com/containers/storage/pkg/regexp/regexp_dontprecompile.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
//go:build !regexp_precompile
|
||||||
|
|
||||||
|
package regexp
|
||||||
|
|
||||||
|
const precompile = false
|
5
vendor/github.com/containers/storage/pkg/regexp/regexp_precompile.go
generated
vendored
Normal file
5
vendor/github.com/containers/storage/pkg/regexp/regexp_precompile.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
//go:build regexp_precompile
|
||||||
|
|
||||||
|
package regexp
|
||||||
|
|
||||||
|
const precompile = true
|
|
@ -0,0 +1,17 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Chmod(name string, mode os.FileMode) error {
|
||||||
|
err := os.Chmod(name, mode)
|
||||||
|
|
||||||
|
for err != nil && errors.Is(err, syscall.EINTR) {
|
||||||
|
err = os.Chmod(name, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Chtimes changes the access time and modified time of a file at the given path
|
||||||
|
func Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||||
|
unixMinTime := time.Unix(0, 0)
|
||||||
|
unixMaxTime := maxTime
|
||||||
|
|
||||||
|
// If the modified time is prior to the Unix Epoch, or after the
|
||||||
|
// end of Unix Time, os.Chtimes has undefined behavior
|
||||||
|
// default to Unix Epoch in this case, just in case
|
||||||
|
|
||||||
|
if atime.Before(unixMinTime) || atime.After(unixMaxTime) {
|
||||||
|
atime = unixMinTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if mtime.Before(unixMinTime) || mtime.After(unixMaxTime) {
|
||||||
|
mtime = unixMinTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Chtimes(name, atime, mtime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take platform specific action for setting create time.
|
||||||
|
if err := setCTime(name, mtime); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setCTime will set the create time on a file. On Unix, the create
|
||||||
|
// time is updated as a side effect of setting the modified time, so
|
||||||
|
// no action is required.
|
||||||
|
func setCTime(path string, ctime time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
28
vendor/github.com/containers/storage/pkg/system/chtimes_windows.go
generated
vendored
Normal file
28
vendor/github.com/containers/storage/pkg/system/chtimes_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setCTime will set the create time on a file. On Windows, this requires
|
||||||
|
// calling SetFileTime and explicitly including the create time.
|
||||||
|
func setCTime(path string, ctime time.Time) error {
|
||||||
|
ctimespec := windows.NsecToTimespec(ctime.UnixNano())
|
||||||
|
pathp, e := windows.UTF16PtrFromString(path)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
h, e := windows.CreateFile(pathp,
|
||||||
|
windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil,
|
||||||
|
windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
defer windows.Close(h)
|
||||||
|
c := windows.NsecToFiletime(windows.TimespecToNsec(ctimespec))
|
||||||
|
return windows.SetFileTime(h, &c, nil, nil)
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNotSupportedPlatform means the platform is not supported.
|
||||||
|
var ErrNotSupportedPlatform = errors.New("platform and architecture is not supported")
|
|
@ -0,0 +1,33 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetExitCode returns the ExitStatus of the specified error if its type is
|
||||||
|
// exec.ExitError, returns 0 and an error otherwise.
|
||||||
|
func GetExitCode(err error) (int, error) {
|
||||||
|
exitCode := 0
|
||||||
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
|
if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
return procExit.ExitStatus(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exitCode, fmt.Errorf("failed to get exit code")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessExitCode process the specified error and returns the exit status code
|
||||||
|
// if the error was of type exec.ExitError, returns nothing otherwise.
|
||||||
|
func ProcessExitCode(err error) (exitCode int) {
|
||||||
|
if err != nil {
|
||||||
|
var exiterr error
|
||||||
|
if exitCode, exiterr = GetExitCode(err); exiterr != nil {
|
||||||
|
// TODO: Fix this so we check the error's text.
|
||||||
|
// we've failed to retrieve exit code, so we set it to 127
|
||||||
|
exitCode = 127
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
93
vendor/github.com/containers/storage/pkg/system/extattr_freebsd.go
generated
vendored
Normal file
93
vendor/github.com/containers/storage/pkg/system/extattr_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
//go:build freebsd
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EXTATTR_NAMESPACE_EMPTY = unix.EXTATTR_NAMESPACE_EMPTY
|
||||||
|
EXTATTR_NAMESPACE_USER = unix.EXTATTR_NAMESPACE_USER
|
||||||
|
EXTATTR_NAMESPACE_SYSTEM = unix.EXTATTR_NAMESPACE_SYSTEM
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtattrGetLink retrieves the value of the extended attribute identified by attrname
|
||||||
|
// in the given namespace and associated with the given path in the file system.
|
||||||
|
// If the path is a symbolic link, the extended attribute is retrieved from the link itself.
|
||||||
|
// Returns a []byte slice if the extattr is set and nil otherwise.
|
||||||
|
func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) {
|
||||||
|
size, errno := unix.ExtattrGetLink(path, attrnamespace, attrname,
|
||||||
|
uintptr(unsafe.Pointer(nil)), 0)
|
||||||
|
if errno != nil {
|
||||||
|
if errno == unix.ENOATTR {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, &os.PathError{Op: "extattr_get_link", Path: path, Err: errno}
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dest := make([]byte, size)
|
||||||
|
size, errno = unix.ExtattrGetLink(path, attrnamespace, attrname,
|
||||||
|
uintptr(unsafe.Pointer(&dest[0])), size)
|
||||||
|
if errno != nil {
|
||||||
|
return nil, &os.PathError{Op: "extattr_get_link", Path: path, Err: errno}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest[:size], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtattrSetLink sets the value of extended attribute identified by attrname
|
||||||
|
// in the given namespace and associated with the given path in the file system.
|
||||||
|
// If the path is a symbolic link, the extended attribute is set on the link itself.
|
||||||
|
func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error {
|
||||||
|
if len(data) == 0 {
|
||||||
|
data = []byte{} // ensure non-nil for empty data
|
||||||
|
}
|
||||||
|
if _, errno := unix.ExtattrSetLink(path, attrnamespace, attrname,
|
||||||
|
uintptr(unsafe.Pointer(&data[0])), len(data)); errno != nil {
|
||||||
|
return &os.PathError{Op: "extattr_set_link", Path: path, Err: errno}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtattrListLink lists extended attributes associated with the given path
|
||||||
|
// in the specified namespace. If the path is a symbolic link, the attributes
|
||||||
|
// are listed from the link itself.
|
||||||
|
func ExtattrListLink(path string, attrnamespace int) ([]string, error) {
|
||||||
|
size, errno := unix.ExtattrListLink(path, attrnamespace,
|
||||||
|
uintptr(unsafe.Pointer(nil)), 0)
|
||||||
|
if errno != nil {
|
||||||
|
return nil, &os.PathError{Op: "extattr_list_link", Path: path, Err: errno}
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dest := make([]byte, size)
|
||||||
|
size, errno = unix.ExtattrListLink(path, attrnamespace,
|
||||||
|
uintptr(unsafe.Pointer(&dest[0])), size)
|
||||||
|
if errno != nil {
|
||||||
|
return nil, &os.PathError{Op: "extattr_list_link", Path: path, Err: errno}
|
||||||
|
}
|
||||||
|
|
||||||
|
var attrs []string
|
||||||
|
for i := 0; i < size; {
|
||||||
|
// Each attribute is preceded by a single byte length
|
||||||
|
length := int(dest[i])
|
||||||
|
i++
|
||||||
|
if i+length > size {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attrs = append(attrs, string(dest[i:i+length]))
|
||||||
|
i += length
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs, nil
|
||||||
|
}
|
24
vendor/github.com/containers/storage/pkg/system/extattr_unsupported.go
generated
vendored
Normal file
24
vendor/github.com/containers/storage/pkg/system/extattr_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//go:build !freebsd
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
const (
|
||||||
|
EXTATTR_NAMESPACE_EMPTY = 0
|
||||||
|
EXTATTR_NAMESPACE_USER = 0
|
||||||
|
EXTATTR_NAMESPACE_SYSTEM = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtattrGetLink is not supported on platforms other than FreeBSD.
|
||||||
|
func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) {
|
||||||
|
return nil, ErrNotSupportedPlatform
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtattrSetLink is not supported on platforms other than FreeBSD.
|
||||||
|
func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error {
|
||||||
|
return ErrNotSupportedPlatform
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtattrListLink is not supported on platforms other than FreeBSD.
|
||||||
|
func ExtattrListLink(path string, attrnamespace int) ([]string, error) {
|
||||||
|
return nil, ErrNotSupportedPlatform
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maxTime is used by chtimes.
|
||||||
|
var maxTime time.Time
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// chtimes initialization
|
||||||
|
if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 {
|
||||||
|
// This is a 64 bit timespec
|
||||||
|
// os.Chtimes limits time to the following
|
||||||
|
maxTime = time.Unix(0, 1<<63-1)
|
||||||
|
} else {
|
||||||
|
// This is a 32 bit timespec
|
||||||
|
maxTime = time.Unix(1<<31-1, 0)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// LCOWSupported determines if Linux Containers on Windows are supported.
|
||||||
|
// Note: This feature is in development (06/17) and enabled through an
|
||||||
|
// environment variable. At a future time, it will be enabled based
|
||||||
|
// on build number. @jhowardmsft
|
||||||
|
var lcowSupported = false
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// LCOW initialization
|
||||||
|
if os.Getenv("LCOW_SUPPORTED") != "" {
|
||||||
|
lcowSupported = true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
//go:build freebsd
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Flag values from <sys/stat.h>
|
||||||
|
const (
|
||||||
|
/*
|
||||||
|
* Definitions of flags stored in file flags word.
|
||||||
|
*
|
||||||
|
* Super-user and owner changeable flags.
|
||||||
|
*/
|
||||||
|
UF_SETTABLE uint32 = 0x0000ffff /* mask of owner changeable flags */
|
||||||
|
UF_NODUMP uint32 = 0x00000001 /* do not dump file */
|
||||||
|
UF_IMMUTABLE uint32 = 0x00000002 /* file may not be changed */
|
||||||
|
UF_APPEND uint32 = 0x00000004 /* writes to file may only append */
|
||||||
|
UF_OPAQUE uint32 = 0x00000008 /* directory is opaque wrt. union */
|
||||||
|
UF_NOUNLINK uint32 = 0x00000010 /* file may not be removed or renamed */
|
||||||
|
|
||||||
|
UF_SYSTEM uint32 = 0x00000080 /* Windows system file bit */
|
||||||
|
UF_SPARSE uint32 = 0x00000100 /* sparse file */
|
||||||
|
UF_OFFLINE uint32 = 0x00000200 /* file is offline */
|
||||||
|
UF_REPARSE uint32 = 0x00000400 /* Windows reparse point file bit */
|
||||||
|
UF_ARCHIVE uint32 = 0x00000800 /* file needs to be archived */
|
||||||
|
UF_READONLY uint32 = 0x00001000 /* Windows readonly file bit */
|
||||||
|
/* This is the same as the MacOS X definition of UF_HIDDEN. */
|
||||||
|
UF_HIDDEN uint32 = 0x00008000 /* file is hidden */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Super-user changeable flags.
|
||||||
|
*/
|
||||||
|
SF_SETTABLE uint32 = 0xffff0000 /* mask of superuser changeable flags */
|
||||||
|
SF_ARCHIVED uint32 = 0x00010000 /* file is archived */
|
||||||
|
SF_IMMUTABLE uint32 = 0x00020000 /* file may not be changed */
|
||||||
|
SF_APPEND uint32 = 0x00040000 /* writes to file may only append */
|
||||||
|
SF_NOUNLINK uint32 = 0x00100000 /* file may not be removed or renamed */
|
||||||
|
SF_SNAPSHOT uint32 = 0x00200000 /* snapshot inode */
|
||||||
|
)
|
||||||
|
|
||||||
|
func Lchflags(path string, flags uint32) error {
|
||||||
|
p, err := unix.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _, e1 := unix.Syscall(unix.SYS_LCHFLAGS, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
|
||||||
|
if e1 != 0 {
|
||||||
|
return e1
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Lchown(name string, uid, gid int) error {
|
||||||
|
err := syscall.Lchown(name, uid, gid)
|
||||||
|
|
||||||
|
for err == syscall.EINTR {
|
||||||
|
err = syscall.Lchown(name, uid, gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return &os.PathError{Op: "lchown", Path: name, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
// LCOWSupported returns true if Linux containers on Windows are supported.
|
||||||
|
func LCOWSupported() bool {
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
// LCOWSupported returns true if Linux containers on Windows are supported.
|
||||||
|
func LCOWSupported() bool {
|
||||||
|
return lcowSupported
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lstat takes a path to a file and returns
|
||||||
|
// a system.StatT type pertaining to that file.
|
||||||
|
//
|
||||||
|
// Throws an error if the file does not exist
|
||||||
|
func Lstat(path string) (*StatT, error) {
|
||||||
|
s := &syscall.Stat_t{}
|
||||||
|
if err := syscall.Lstat(path, s); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "Lstat", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return fromStatT(s)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// Lstat calls os.Lstat to get a fileinfo interface back.
|
||||||
|
// This is then copied into our own locally defined structure.
|
||||||
|
func Lstat(path string) (*StatT, error) {
|
||||||
|
fi, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromStatT(&fi)
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
// MemInfo contains memory statistics of the host system.
|
||||||
|
type MemInfo struct {
|
||||||
|
// Total usable RAM (i.e. physical RAM minus a few reserved bits and the
|
||||||
|
// kernel binary code).
|
||||||
|
MemTotal int64
|
||||||
|
|
||||||
|
// Amount of free memory.
|
||||||
|
MemFree int64
|
||||||
|
|
||||||
|
// Total amount of swap space available.
|
||||||
|
SwapTotal int64
|
||||||
|
|
||||||
|
// Amount of swap space that is currently unused.
|
||||||
|
SwapFree int64
|
||||||
|
}
|
85
vendor/github.com/containers/storage/pkg/system/meminfo_freebsd.go
generated
vendored
Normal file
85
vendor/github.com/containers/storage/pkg/system/meminfo_freebsd.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
//go:build freebsd && cgo
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #include <unistd.h>
|
||||||
|
// #include <sys/vmmeter.h>
|
||||||
|
// #include <sys/sysctl.h>
|
||||||
|
// #include <vm/vm_param.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func getMemInfo() (int64, int64, error) {
|
||||||
|
data, err := unix.SysctlRaw("vm.vmtotal")
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("can't get kernel info: %w", err)
|
||||||
|
}
|
||||||
|
if len(data) != C.sizeof_struct_vmtotal {
|
||||||
|
return -1, -1, fmt.Errorf("unexpected vmtotal size %d", len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
total := (*C.struct_vmtotal)(unsafe.Pointer(&data[0]))
|
||||||
|
|
||||||
|
pagesize := int64(C.sysconf(C._SC_PAGESIZE))
|
||||||
|
npages := int64(C.sysconf(C._SC_PHYS_PAGES))
|
||||||
|
return pagesize * npages, pagesize * int64(total.t_free), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSwapInfo() (int64, int64, error) {
|
||||||
|
var (
|
||||||
|
total int64 = 0
|
||||||
|
used int64 = 0
|
||||||
|
)
|
||||||
|
swapCount, err := unix.SysctlUint32("vm.nswapdev")
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("reading vm.nswapdev: %w", err)
|
||||||
|
}
|
||||||
|
for i := 0; i < int(swapCount); i++ {
|
||||||
|
data, err := unix.SysctlRaw("vm.swap_info", i)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("reading vm.swap_info.%d: %w", i, err)
|
||||||
|
}
|
||||||
|
if len(data) != C.sizeof_struct_xswdev {
|
||||||
|
return -1, -1, fmt.Errorf("unexpected swap_info size %d", len(data))
|
||||||
|
}
|
||||||
|
xsw := (*C.struct_xswdev)(unsafe.Pointer(&data[0]))
|
||||||
|
total += int64(xsw.xsw_nblks)
|
||||||
|
used += int64(xsw.xsw_used)
|
||||||
|
}
|
||||||
|
pagesize := int64(C.sysconf(C._SC_PAGESIZE))
|
||||||
|
return pagesize * total, pagesize * (total - used), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMemInfo retrieves memory statistics of the host system and returns a
|
||||||
|
//
|
||||||
|
// MemInfo type.
|
||||||
|
func ReadMemInfo() (*MemInfo, error) {
|
||||||
|
MemTotal, MemFree, err := getMemInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting memory totals %w", err)
|
||||||
|
}
|
||||||
|
SwapTotal, SwapFree, err := getSwapInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting swap totals %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if MemTotal < 0 || MemFree < 0 || SwapTotal < 0 || SwapFree < 0 {
|
||||||
|
return nil, errors.New("getting system memory info")
|
||||||
|
}
|
||||||
|
|
||||||
|
meminfo := &MemInfo{}
|
||||||
|
// Total memory is total physical memory less than memory locked by kernel
|
||||||
|
meminfo.MemTotal = MemTotal
|
||||||
|
meminfo.MemFree = MemFree
|
||||||
|
meminfo.SwapTotal = SwapTotal
|
||||||
|
meminfo.SwapFree = SwapFree
|
||||||
|
|
||||||
|
return meminfo, nil
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadMemInfo retrieves memory statistics of the host system and returns a
|
||||||
|
// MemInfo type.
|
||||||
|
func ReadMemInfo() (*MemInfo, error) {
|
||||||
|
file, err := os.Open("/proc/meminfo")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
return parseMemInfo(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMemInfo parses the /proc/meminfo file into
|
||||||
|
// a MemInfo object given an io.Reader to the file.
|
||||||
|
// Throws error if there are problems reading from the file
|
||||||
|
func parseMemInfo(reader io.Reader) (*MemInfo, error) {
|
||||||
|
meminfo := &MemInfo{}
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
for scanner.Scan() {
|
||||||
|
// Expected format: ["MemTotal:", "1234", "kB"]
|
||||||
|
parts := strings.Fields(scanner.Text())
|
||||||
|
|
||||||
|
// Sanity checks: Skip malformed entries.
|
||||||
|
if len(parts) < 3 || parts[2] != "kB" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to bytes.
|
||||||
|
size, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bytes := int64(size) * units.KiB
|
||||||
|
|
||||||
|
switch parts[0] {
|
||||||
|
case "MemTotal:":
|
||||||
|
meminfo.MemTotal = bytes
|
||||||
|
case "MemFree:":
|
||||||
|
meminfo.MemFree = bytes
|
||||||
|
case "SwapTotal:":
|
||||||
|
meminfo.SwapTotal = bytes
|
||||||
|
case "SwapFree:":
|
||||||
|
meminfo.SwapFree = bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle errors that may have occurred during the reading of the file.
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return meminfo, nil
|
||||||
|
}
|
129
vendor/github.com/containers/storage/pkg/system/meminfo_solaris.go
generated
vendored
Normal file
129
vendor/github.com/containers/storage/pkg/system/meminfo_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
//go:build solaris && cgo
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #cgo CFLAGS: -std=c99
|
||||||
|
// #cgo LDFLAGS: -lkstat
|
||||||
|
// #include <unistd.h>
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include <stdio.h>
|
||||||
|
// #include <kstat.h>
|
||||||
|
// #include <sys/swap.h>
|
||||||
|
// #include <sys/param.h>
|
||||||
|
// struct swaptable *allocSwaptable(int num) {
|
||||||
|
// struct swaptable *st;
|
||||||
|
// struct swapent *swapent;
|
||||||
|
// st = (struct swaptable *)malloc(num * sizeof(swapent_t) + sizeof (int));
|
||||||
|
// swapent = st->swt_ent;
|
||||||
|
// for (int i = 0; i < num; i++,swapent++) {
|
||||||
|
// swapent->ste_path = (char *)malloc(MAXPATHLEN * sizeof (char));
|
||||||
|
// }
|
||||||
|
// st->swt_n = num;
|
||||||
|
// return st;
|
||||||
|
//}
|
||||||
|
// void freeSwaptable (struct swaptable *st) {
|
||||||
|
// struct swapent *swapent = st->swt_ent;
|
||||||
|
// for (int i = 0; i < st->swt_n; i++,swapent++) {
|
||||||
|
// free(swapent->ste_path);
|
||||||
|
// }
|
||||||
|
// free(st);
|
||||||
|
// }
|
||||||
|
// swapent_t getSwapEnt(swapent_t *ent, int i) {
|
||||||
|
// return ent[i];
|
||||||
|
// }
|
||||||
|
// int64_t getPpKernel() {
|
||||||
|
// int64_t pp_kernel = 0;
|
||||||
|
// kstat_ctl_t *ksc;
|
||||||
|
// kstat_t *ks;
|
||||||
|
// kstat_named_t *knp;
|
||||||
|
// kid_t kid;
|
||||||
|
//
|
||||||
|
// if ((ksc = kstat_open()) == NULL) {
|
||||||
|
// return -1;
|
||||||
|
// }
|
||||||
|
// if ((ks = kstat_lookup(ksc, "unix", 0, "system_pages")) == NULL) {
|
||||||
|
// return -1;
|
||||||
|
// }
|
||||||
|
// if (((kid = kstat_read(ksc, ks, NULL)) == -1) ||
|
||||||
|
// ((knp = kstat_data_lookup(ks, "pp_kernel")) == NULL)) {
|
||||||
|
// return -1;
|
||||||
|
// }
|
||||||
|
// switch (knp->data_type) {
|
||||||
|
// case KSTAT_DATA_UINT64:
|
||||||
|
// pp_kernel = knp->value.ui64;
|
||||||
|
// break;
|
||||||
|
// case KSTAT_DATA_UINT32:
|
||||||
|
// pp_kernel = knp->value.ui32;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// pp_kernel *= sysconf(_SC_PAGESIZE);
|
||||||
|
// return (pp_kernel > 0 ? pp_kernel : -1);
|
||||||
|
// }
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Get the system memory info using sysconf same as prtconf
|
||||||
|
func getTotalMem() int64 {
|
||||||
|
pagesize := C.sysconf(C._SC_PAGESIZE)
|
||||||
|
npages := C.sysconf(C._SC_PHYS_PAGES)
|
||||||
|
return int64(pagesize * npages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFreeMem() int64 {
|
||||||
|
pagesize := C.sysconf(C._SC_PAGESIZE)
|
||||||
|
npages := C.sysconf(C._SC_AVPHYS_PAGES)
|
||||||
|
return int64(pagesize * npages)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMemInfo retrieves memory statistics of the host system and returns a
|
||||||
|
//
|
||||||
|
// MemInfo type.
|
||||||
|
func ReadMemInfo() (*MemInfo, error) {
|
||||||
|
ppKernel := C.getPpKernel()
|
||||||
|
MemTotal := getTotalMem()
|
||||||
|
MemFree := getFreeMem()
|
||||||
|
SwapTotal, SwapFree, err := getSysSwap()
|
||||||
|
|
||||||
|
if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 ||
|
||||||
|
SwapFree < 0 {
|
||||||
|
return nil, fmt.Errorf("getting system memory info %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
meminfo := &MemInfo{}
|
||||||
|
// Total memory is total physical memory less than memory locked by kernel
|
||||||
|
meminfo.MemTotal = MemTotal - int64(ppKernel)
|
||||||
|
meminfo.MemFree = MemFree
|
||||||
|
meminfo.SwapTotal = SwapTotal
|
||||||
|
meminfo.SwapFree = SwapFree
|
||||||
|
|
||||||
|
return meminfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSysSwap() (int64, int64, error) {
|
||||||
|
var tSwap int64
|
||||||
|
var fSwap int64
|
||||||
|
var diskblksPerPage int64
|
||||||
|
num, err := C.swapctl(C.SC_GETNSWP, nil)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
st := C.allocSwaptable(num)
|
||||||
|
_, err = C.swapctl(C.SC_LIST, unsafe.Pointer(st))
|
||||||
|
if err != nil {
|
||||||
|
C.freeSwaptable(st)
|
||||||
|
return -1, -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
diskblksPerPage = int64(C.sysconf(C._SC_PAGESIZE) >> C.DEV_BSHIFT)
|
||||||
|
for i := 0; i < int(num); i++ {
|
||||||
|
swapent := C.getSwapEnt(&st.swt_ent[0], C.int(i))
|
||||||
|
tSwap += int64(swapent.ste_pages) * diskblksPerPage
|
||||||
|
fSwap += int64(swapent.ste_free) * diskblksPerPage
|
||||||
|
}
|
||||||
|
C.freeSwaptable(st)
|
||||||
|
return tSwap, fSwap, nil
|
||||||
|
}
|
8
vendor/github.com/containers/storage/pkg/system/meminfo_unsupported.go
generated
vendored
Normal file
8
vendor/github.com/containers/storage/pkg/system/meminfo_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
//go:build !linux && !windows && !solaris && !(freebsd && cgo)
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
// ReadMemInfo is not supported on platforms other than linux and windows.
|
||||||
|
func ReadMemInfo() (*MemInfo, error) {
|
||||||
|
return nil, ErrNotSupportedPlatform
|
||||||
|
}
|
46
vendor/github.com/containers/storage/pkg/system/meminfo_windows.go
generated
vendored
Normal file
46
vendor/github.com/containers/storage/pkg/system/meminfo_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
|
||||||
|
procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx")
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx
|
||||||
|
type memorystatusex struct {
|
||||||
|
dwLength uint32
|
||||||
|
dwMemoryLoad uint32
|
||||||
|
ullTotalPhys uint64
|
||||||
|
ullAvailPhys uint64
|
||||||
|
ullTotalPageFile uint64
|
||||||
|
ullAvailPageFile uint64
|
||||||
|
ullTotalVirtual uint64
|
||||||
|
ullAvailVirtual uint64
|
||||||
|
ullAvailExtendedVirtual uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMemInfo retrieves memory statistics of the host system and returns a
|
||||||
|
//
|
||||||
|
// MemInfo type.
|
||||||
|
func ReadMemInfo() (*MemInfo, error) {
|
||||||
|
msi := &memorystatusex{
|
||||||
|
dwLength: 64,
|
||||||
|
}
|
||||||
|
r1, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msi)))
|
||||||
|
if r1 == 0 {
|
||||||
|
return &MemInfo{}, nil
|
||||||
|
}
|
||||||
|
return &MemInfo{
|
||||||
|
MemTotal: int64(msi.ullTotalPhys),
|
||||||
|
MemFree: int64(msi.ullAvailPhys),
|
||||||
|
SwapTotal: int64(msi.ullTotalPageFile),
|
||||||
|
SwapFree: int64(msi.ullAvailPageFile),
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//go:build !windows && !freebsd
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mknod creates a filesystem node (file, device special file or named pipe) named path
|
||||||
|
// with attributes specified by mode and dev.
|
||||||
|
func Mknod(path string, mode uint32, dev uint32) error {
|
||||||
|
return unix.Mknod(path, mode, int(dev))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdev is used to build the value of linux devices (in /dev/) which specifies major
|
||||||
|
// and minor number of the newly created device special file.
|
||||||
|
// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.
|
||||||
|
// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major,
|
||||||
|
// then the top 12 bits of the minor.
|
||||||
|
func Mkdev(major int64, minor int64) uint32 {
|
||||||
|
return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//go:build freebsd
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mknod creates a filesystem node (file, device special file or named pipe) named path
|
||||||
|
// with attributes specified by mode and dev.
|
||||||
|
func Mknod(path string, mode uint32, dev uint64) error {
|
||||||
|
return unix.Mknod(path, mode, dev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdev is used to build the value of linux devices (in /dev/) which specifies major
|
||||||
|
// and minor number of the newly created device special file.
|
||||||
|
// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.
|
||||||
|
// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major,
|
||||||
|
// then the top 12 bits of the minor.
|
||||||
|
func Mkdev(major int64, minor int64) uint64 {
|
||||||
|
return uint64(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
// Mknod is not implemented on Windows.
|
||||||
|
func Mknod(path string, mode uint32, dev int) error {
|
||||||
|
return ErrNotSupportedPlatform
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdev is not implemented on Windows.
|
||||||
|
func Mkdev(major int64, minor int64) uint32 {
|
||||||
|
panic("Mkdev not implemented on Windows.")
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
const defaultUnixPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
|
||||||
|
// DefaultPathEnv is unix style list of directories to search for
|
||||||
|
// executables. Each directory is separated from the next by a colon
|
||||||
|
// ':' character .
|
||||||
|
func DefaultPathEnv(platform string) string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if platform != runtime.GOOS && LCOWSupported() {
|
||||||
|
return defaultUnixPathEnv
|
||||||
|
}
|
||||||
|
// Deliberately empty on Windows containers on Windows as the default path will be set by
|
||||||
|
// the container. Docker has no context of what the default path should be.
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return defaultUnixPathEnv
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
// CheckSystemDriveAndRemoveDriveLetter verifies that a path, if it includes a drive letter,
|
||||||
|
// is the system drive. This is a no-op on Linux.
|
||||||
|
func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) {
|
||||||
|
return path, nil
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path.
|
||||||
|
// This is used, for example, when validating a user provided path in docker cp.
|
||||||
|
// If a drive letter is supplied, it must be the system drive. The drive letter
|
||||||
|
// is always removed. Also, it translates it to OS semantics (IOW / to \). We
|
||||||
|
// need the path in this syntax so that it can ultimately be concatenated with
|
||||||
|
// a Windows long-path which doesn't support drive-letters. Examples:
|
||||||
|
// C: --> Fail
|
||||||
|
// C:\ --> \
|
||||||
|
// a --> a
|
||||||
|
// /a --> \a
|
||||||
|
// d:\ --> Fail
|
||||||
|
func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) {
|
||||||
|
if len(path) == 2 && string(path[1]) == ":" {
|
||||||
|
return "", fmt.Errorf("relative path not specified in %q", path)
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(path) || len(path) < 2 {
|
||||||
|
return filepath.FromSlash(path), nil
|
||||||
|
}
|
||||||
|
if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") {
|
||||||
|
return "", fmt.Errorf("specified path is not on the system drive (C:)")
|
||||||
|
}
|
||||||
|
return filepath.FromSlash(path[2:]), nil
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
//go:build linux || freebsd || solaris || darwin
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsProcessAlive returns true if process with a given pid is running.
|
||||||
|
func IsProcessAlive(pid int) bool {
|
||||||
|
err := unix.Kill(pid, syscall.Signal(0))
|
||||||
|
if err == nil || err == unix.EPERM {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// KillProcess force-stops a process.
|
||||||
|
func KillProcess(pid int) {
|
||||||
|
_ = unix.Kill(pid, unix.SIGKILL)
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/mount"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
|
||||||
|
// often be remedied.
|
||||||
|
// Only use `EnsureRemoveAll` if you really want to make every effort to remove
|
||||||
|
// a directory.
|
||||||
|
//
|
||||||
|
// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
|
||||||
|
// can be a race between reading directory entries and then actually attempting
|
||||||
|
// to remove everything in the directory.
|
||||||
|
// These types of errors do not need to be returned since it's ok for the dir to
|
||||||
|
// be gone we can just retry the remove operation.
|
||||||
|
//
|
||||||
|
// This should not return a `os.ErrNotExist` kind of error under any circumstances
|
||||||
|
func EnsureRemoveAll(dir string) error {
|
||||||
|
notExistErr := make(map[string]bool)
|
||||||
|
|
||||||
|
// track retries
|
||||||
|
exitOnErr := make(map[string]int)
|
||||||
|
maxRetry := 1000
|
||||||
|
|
||||||
|
// Attempt a simple remove all first, this avoids the more expensive
|
||||||
|
// RecursiveUnmount call if not needed.
|
||||||
|
if err := os.RemoveAll(dir); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to unmount anything beneath this dir first
|
||||||
|
if err := mount.RecursiveUnmount(dir); err != nil {
|
||||||
|
logrus.Debugf("RecursiveUnmount on %s failed: %v", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
err := os.RemoveAll(dir)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the RemoveAll fails with a permission error, we
|
||||||
|
// may have immutable files so try to remove the
|
||||||
|
// immutable flag and redo the RemoveAll.
|
||||||
|
if errors.Is(err, syscall.EPERM) {
|
||||||
|
if err = resetFileFlags(dir); err != nil {
|
||||||
|
return fmt.Errorf("resetting file flags: %w", err)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(dir)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pe, ok := err.(*os.PathError)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if notExistErr[pe.Path] {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
notExistErr[pe.Path] = true
|
||||||
|
|
||||||
|
// There is a race where some subdir can be removed but after the parent
|
||||||
|
// dir entries have been read.
|
||||||
|
// So the path could be from `os.Remove(subdir)`
|
||||||
|
// If the reported non-existent path is not the passed in `dir` we
|
||||||
|
// should just retry, but otherwise return with no error.
|
||||||
|
if pe.Path == dir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsEBUSY(pe.Err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if e := mount.Unmount(pe.Path); e != nil {
|
||||||
|
return fmt.Errorf("while removing %s: %w", dir, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exitOnErr[pe.Path] == maxRetry {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
exitOnErr[pe.Path]++
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue