Compare commits

...

146 Commits
v1.2.8 ... main

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

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

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

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

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

Signed-off-by: Domenico Andreoli <domenico.andreoli@linux.com>
2024-05-22 15:34:48 +02:00
Valentin Rothberg 681179f1bd
Merge pull request #163 from containers/renovate/github.com-stretchr-testify-1.x
fix(deps): update module github.com/stretchr/testify to v1.9.0
2024-03-04 09:06:01 +01:00
renovate[bot] b486e8181e
fix(deps): update module github.com/stretchr/testify to v1.9.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-02 14:01:54 +00:00
Valentin Rothberg cd907412d5
Merge pull request #158 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.52.0
2024-02-16 12:56:33 +01:00
renovate[bot] 969537eb7a
fix(deps): update module github.com/containers/storage to v1.52.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-16 09:11:29 +00:00
Valentin Rothberg ec31ff1e86
Merge pull request #162 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
2024-02-16 10:10:17 +01:00
renovate[bot] fe7a74ceed
fix(deps): update module github.com/opencontainers/runtime-spec to v1.2.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 16:32:20 +00:00
Valentin Rothberg 01ca720a78
Merge pull request #161 from containers/renovate/go-github.com/opencontainers/runc-vulnerability
chore(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
2024-02-05 12:04:14 +01:00
Valentin Rothberg 57e5d60a3f
Merge pull request #156 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.57.4
2024-02-05 12:03:54 +01:00
renovate[bot] 062cbb9697
fix(deps): update module github.com/containers/common to v0.57.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 10:52:12 +00:00
renovate[bot] 12a6b5a7ef
chore(deps): update module github.com/opencontainers/runc to v1.1.12 [security]
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 10:51:27 +00:00
Valentin Rothberg d7918b036c
Merge pull request #160 from vrothberg/update-ci-image
update CI images
2024-02-05 11:51:00 +01:00
Valentin Rothberg 8b59acfd3d bump golangci-lint to 1.55.2
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2024-02-05 10:33:54 +01:00
Valentin Rothberg 3082eed8f1
Merge pull request #155 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.57.0
2023-11-17 10:17:21 +01:00
Valentin Rothberg a4c0f838cd
Merge pull request #152 from containers/renovate/major-ci-vm-image
chore(deps): update dependency containers/automation_images to v20231116
2023-11-17 10:16:48 +01:00
renovate[bot] fff84e808d
fix(deps): update module github.com/containers/common to v0.57.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-17 00:05:25 +00:00
renovate[bot] 5ed5002d6c
chore(deps): update dependency containers/automation_images to v20231116
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 21:48:52 +00:00
Valentin Rothberg ad9b137be0
Merge pull request #153 from vrothberg/release
bump to v1.2.10
2023-10-20 10:49:42 +02:00
Valentin Rothberg 1c78fe1b15 bump to v1.2.11-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-10-20 10:36:15 +02:00
Valentin Rothberg a7bafcb864 v1.2.10
* cirrus: vendor: use go 1.19
* vendor c/common@v0.56.0
* bump module to go 1.19
* Update module github.com/containers/common to v0.55.4
* Update module github.com/containers/storage to v1.48.1
* Update module github.com/containers/common to v0.55.3
* Update dependency containers/automation_images to v20230816
* Update dependency containers/automation_images to v20230807
* Update module github.com/opencontainers/runtime-spec to v1.1.0
* Update module github.com/containers/common to v0.55.2
* Update module github.com/containers/common to v0.55.1
* Update module github.com/containers/common to v0.55.0
* Update module github.com/containers/storage to v1.48.0
* Update module github.com/containers/common to v0.54.0
* Update module github.com/containers/storage to v1.47.0
* Update dependency containers/automation_images to v20230614
* Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
* Update module github.com/stretchr/testify to v1.8.4
* Update module github.com/sirupsen/logrus to v1.9.3
* Update dependency containers/automation_images to v20230601
* Update module github.com/sirupsen/logrus to v1.9.2
* Update module github.com/stretchr/testify to v1.8.3
* Bump golangci-lint to version 1.51.2
* Cirrus: Enable testing on F38 + Rawhide
* Update module github.com/sirupsen/logrus to v1.9.1
* Update module github.com/containers/common to v0.53.0
* check root privileges
* vendor c/common@v0.52.0
* Update dependency containers/automation_images to v20230405

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-10-20 10:35:17 +02:00
Valentin Rothberg 8cdee7b11e
Merge pull request #150 from vrothberg/vendor
vendor c/common@v0.56.0
2023-09-18 09:55:25 +02:00
Valentin Rothberg e8f359faac cirrus: vendor: use go 1.19
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-09-18 09:09:01 +02:00
Valentin Rothberg c6dd74b3e7 vendor c/common@v0.56.0
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-09-18 09:05:40 +02:00
Valentin Rothberg 86c18727bb bump module to go 1.19
Transitive dependencies already require it.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-09-18 09:05:15 +02:00
Valentin Rothberg 162cf0d113
Merge pull request #148 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.4
2023-08-25 08:49:45 +02:00
renovate[bot] c23a2764ac
Update module github.com/containers/common to v0.55.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-24 17:44:51 +00:00
Valentin Rothberg 3cbd5e812a
Merge pull request #146 from containers/renovate/github.com-containers-storage-1.x
Update module github.com/containers/storage to v1.48.1
2023-08-22 13:18:46 +02:00
renovate[bot] e8062f8abe
Update module github.com/containers/storage to v1.48.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-22 11:06:26 +00:00
Valentin Rothberg a12ae46058
Merge pull request #144 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.3
2023-08-22 13:05:49 +02:00
renovate[bot] a868c45bf8
Update module github.com/containers/common to v0.55.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-22 10:53:35 +00:00
Valentin Rothberg 32c22ee33f
Merge pull request #145 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230816
2023-08-22 12:52:52 +02:00
renovate[bot] 560bae97b8
Update dependency containers/automation_images to v20230816
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-16 23:12:53 +00:00
Valentin Rothberg 8c709c228c
Merge pull request #143 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230807
2023-08-11 13:51:35 +02:00
renovate[bot] d560060775
Update dependency containers/automation_images to v20230807
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-07 20:24:55 +00:00
Valentin Rothberg 21d936bbef
Merge pull request #142 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
Update module github.com/opencontainers/runtime-spec to v1.1.0
2023-07-24 09:13:27 +02:00
renovate[bot] fb98b83d20
Update module github.com/opencontainers/runtime-spec to v1.1.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-22 07:01:39 +00:00
Valentin Rothberg 61c7d317ea
Merge pull request #141 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.2
2023-07-17 08:09:21 +02:00
renovate[bot] 55a5929566
Update module github.com/containers/common to v0.55.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-13 10:28:26 +00:00
Valentin Rothberg e98e224fab
Merge pull request #140 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.1
2023-07-07 08:57:31 +02:00
renovate[bot] 56d0907d7d
Update module github.com/containers/common to v0.55.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-30 15:53:33 +00:00
Valentin Rothberg 9e495e0a80
Merge pull request #139 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.55.0
2023-06-30 10:15:01 +02:00
renovate[bot] 9745218df4
Update module github.com/containers/common to v0.55.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-30 07:53:58 +00:00
Valentin Rothberg 16395c5ed7
Merge pull request #138 from containers/renovate/github.com-containers-storage-1.x
Update module github.com/containers/storage to v1.48.0
2023-06-30 09:52:46 +02:00
renovate[bot] 8a9af6861b
Update module github.com/containers/storage to v1.48.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-29 19:07:25 +00:00
Valentin Rothberg 8c5ed87d79
Merge pull request #137 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.54.0
2023-06-29 09:14:31 +02:00
renovate[bot] 5d4d04f251
Update module github.com/containers/common to v0.54.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-28 21:55:48 +00:00
Valentin Rothberg ce7d972ba4
Merge pull request #136 from containers/renovate/github.com-containers-storage-1.x
Update module github.com/containers/storage to v1.47.0
2023-06-27 08:16:01 +02:00
renovate[bot] 8c4b4dcc6b
Update module github.com/containers/storage to v1.47.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-27 00:12:04 +00:00
Valentin Rothberg 32e2606d27
Merge pull request #130 from containers/renovate/github.com-stretchr-testify-1.x
Update module github.com/stretchr/testify to v1.8.4
2023-06-15 09:15:16 +02:00
Valentin Rothberg 38f6ab6090
Merge pull request #134 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
2023-06-15 09:14:59 +02:00
Valentin Rothberg 5e3793b640
Merge pull request #135 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230614
2023-06-15 09:14:35 +02:00
renovate[bot] 57823f9c42
Update dependency containers/automation_images to v20230614
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-14 15:37:15 +00:00
renovate[bot] 4cc0634fc1
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-10 10:10:42 +00:00
renovate[bot] 1c8eaf3085
Update module github.com/stretchr/testify to v1.8.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 13:28:01 +00:00
Daniel J Walsh d525ee9695
Merge pull request #132 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230601
2023-06-08 07:10:16 -04:00
Daniel J Walsh 37fbd3020f
Merge pull request #133 from containers/renovate/github.com-sirupsen-logrus-1.x
Update module github.com/sirupsen/logrus to v1.9.3
2023-06-08 07:09:47 -04:00
renovate[bot] cf1b3ce101
Update module github.com/sirupsen/logrus to v1.9.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-03 21:19:56 +00:00
renovate[bot] 3b5d827ce8
Update dependency containers/automation_images to v20230601
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-01 18:44:43 +00:00
Valentin Rothberg 967fea3e8e
Merge pull request #127 from containers/renovate/github.com-sirupsen-logrus-1.x
Update module github.com/sirupsen/logrus to v1.9.2
2023-05-22 11:12:59 +02:00
renovate[bot] 39ea9e139f
Update module github.com/sirupsen/logrus to v1.9.2
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-05-22 09:03:40 +00:00
Valentin Rothberg 6fdae39069
Merge pull request #122 from cevich/enable_rawhide_testing
Cirrus: Enable testing on F38 + Rawhide
2023-05-22 11:02:19 +02:00
Valentin Rothberg 28d462ed74
Merge pull request #129 from containers/renovate/github.com-stretchr-testify-1.x
Update module github.com/stretchr/testify to v1.8.3
2023-05-22 11:01:12 +02:00
renovate[bot] 4e983ea172
Update module github.com/stretchr/testify to v1.8.3
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-05-19 03:11:41 +00:00
Chris Evich 89503c8f6b
Bump golangci-lint to version 1.51.2
This is required for compatibility with golang 1.20.  For any version
lower than this, the compiler will panic or fail to build with an error
like: `StringData not declared by package unsafe (typecheck)`

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-05-17 12:21:32 -04:00
Chris Evich ab5d270372
Cirrus: Enable testing on F38 + Rawhide
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-05-17 12:21:31 -04:00
Valentin Rothberg c5eecd3be7
Merge pull request #126 from containers/renovate/github.com-sirupsen-logrus-1.x
Update module github.com/sirupsen/logrus to v1.9.1
2023-05-17 09:44:40 +02:00
renovate[bot] d131bebcca
Update module github.com/sirupsen/logrus to v1.9.1
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-05-16 22:50:44 +00:00
Valentin Rothberg c3a431d834
Merge pull request #124 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.53.0
2023-05-16 11:40:13 +02:00
renovate[bot] 3718eaafa4
Update module github.com/containers/common to v0.53.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-04-27 07:51:54 +00:00
Valentin Rothberg b09d71ad89
Merge pull request #121 from vrothberg/fix-103
check root privileges
2023-04-19 09:45:51 +02:00
Valentin Rothberg 9ca3f9f7b5 check root privileges
Throw an error when the hook isn't executed as root.

Fixes: #103
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-19 09:10:40 +02:00
Valentin Rothberg 3280db1a60
Merge pull request #120 from vrothberg/vendor
vendor c/common@v0.52.0
2023-04-19 08:58:51 +02:00
Valentin Rothberg f1cc6af344
Merge pull request #118 from containers/renovate/major-ci-vm-image
Update dependency containers/automation_images to v20230405
2023-04-19 08:40:01 +02:00
Valentin Rothberg 00e4b496e1 vendor c/common@v0.52.0
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-19 08:39:10 +02:00
renovate[bot] 28f1b41688
Update dependency containers/automation_images to v20230405
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-04-18 18:52:21 +00:00
Valentin Rothberg 82113f6b3a
Merge pull request #117 from vrothberg/bump
bump to v1.2.9
2023-04-18 17:21:34 +02:00
Valentin Rothberg b9068bfb01 bump back to v1.2.10-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-18 17:14:36 +02:00
Valentin Rothberg 3f6c6065a8 v1.2.9
* Cirrus: Always collect journal after testing
* Cirrus: Update CI VM Images
* fix builds on recent kernels
* Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
* Update module github.com/stretchr/testify to v1.8.2
* Use the latest fedora base image
* Add renovate configuration
* bump dependencies
* Updates for golang 1.19
* Cirrus: Update CI VM images to F37
* bump to v1.2.9-dev

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-18 17:13:56 +02:00
Valentin Rothberg 7021239842
Merge pull request #109 from cevich/update_images
Cirrus: Update CI VM Images
2023-04-18 17:12:23 +02:00
Chris Evich bed5713b5a
Cirrus: Always collect journal after testing
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-18 10:57:21 -04:00
Chris Evich 19d707a515
Cirrus: Update CI VM Images
Also update env. var. name for consistency with other repos.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-18 10:57:21 -04:00
Valentin Rothberg 9ee6f0a8ba
Merge pull request #116 from vrothberg/fix-107
fix builds on recent kernels
2023-04-18 16:51:47 +02:00
Valentin Rothberg bc40b01a56 fix builds on recent kernels
Including bpf.h causes inexplicable farts but I am happy it's working
now.

Fixes: #107
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-04-18 16:43:05 +02:00
Valentin Rothberg 8cc1f7aedd
Merge pull request #115 from containers/renovate/github.com-opencontainers-runtime-spec-1.x
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
2023-04-18 14:37:32 +02:00
renovate[bot] bd33a9cdce
Update module github.com/opencontainers/runtime-spec to v1.1.0-rc.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-18 00:27:03 +00:00
Daniel J Walsh e6accd4cfe
Merge pull request #112 from containers/renovate/github.com-stretchr-testify-1.x
Update module github.com/stretchr/testify to v1.8.2
2023-04-14 05:19:25 -04:00
renovate[bot] 544c991c77
Update module github.com/stretchr/testify to v1.8.2
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-04-14 09:08:15 +00:00
Valentin Rothberg 1003459cbc
Merge pull request #111 from containers/renovate/configure
Configure Renovate
2023-04-14 11:07:00 +02:00
Chris Evich 36a96f5d5a
Use the latest fedora base image
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-13 15:14:25 -04:00
renovate[bot] 8013aee634
Add renovate configuration
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-04-13 14:57:48 -04:00
Valentin Rothberg a742ba49c8
Merge pull request #110 from vrothberg/deps
bump dependencies
2023-03-17 10:11:56 +01:00
Valentin Rothberg cc4650c778 bump dependencies
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2023-03-16 12:01:45 +01:00
Valentin Rothberg 872fc48dce
Merge pull request #106 from cevich/F37_ci_vm_images
Cirrus: Update CI VM images to F37
2023-01-19 15:41:09 +01:00
Chris Evich c9476b811f
Updates for golang 1.19
Thanks to @vrothberg for the patch.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-17 11:38:12 -05:00
Chris Evich d6a8b8c7bf
Cirrus: Update CI VM images to F37
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-17 10:34:09 -05:00
Daniel J Walsh b7030ae105
Merge pull request #105 from vrothberg/version
bump to v1.2.8
2022-10-17 09:16:33 -04:00
Valentin Rothberg 2258f0a2ce bump to v1.2.9-dev
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-10-12 09:57:39 +02:00
554 changed files with 59397 additions and 15410 deletions

View File

@ -11,8 +11,8 @@ env:
GOCACHE: "${HOME}/.cache/go-build"
# VM Image built in containers/automation_images
_BUILT_IMAGE_SUFFIX: "c5878804328480768"
FEDORA_CACHE_IMAGE_NAME: "fedora-${_BUILT_IMAGE_SUFFIX}"
IMAGE_SUFFIX: "c20250324t111922z-f41f40d13"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
# Must be defined true when testing w/in containers
CONTAINER: "false"
@ -21,16 +21,6 @@ env:
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
meta_task:
@ -58,7 +48,7 @@ meta_task:
vendor_task:
container:
image: golang:1.18
image: golang:1.23
script:
- make vendor
@ -68,7 +58,17 @@ vendor_task:
build_and_test_task:
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
gocache_cache:
@ -100,6 +100,10 @@ build_and_test_task:
- make test-unit
- sudo make test-integration
# The hook runs in the background, errors will be in the journal.
always:
journal_script: journalctl -b
binaries_artifacts:
path: "bin/*"
type: "application/octet-stream"

54
.github/renovate.json5 vendored Normal file
View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
FROM fedora:31
FROM fedora:latest
ENV GOPATH=/var/tmp/go
ENV GOSRC=$GOPATH/src/github.com/containers/oci-seccomp-bpf-hook

View File

@ -36,12 +36,6 @@ fi
GOSRC="${GOSRC:-$(realpath $(dirname $0)/../../)}"
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
CONTAINER="${CONTAINER:-false}"

View File

@ -18,7 +18,6 @@ type event struct {
// Complete documentation is available at
// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
const source string = `
#include <linux/bpf.h>
#include <linux/nsproxy.h>
#include <linux/pid_namespace.h>
#include <linux/ns_common.h>
@ -112,6 +111,13 @@ int enter_trace(struct tracepoint__raw_syscalls__sys_enter* args)
// The syscall was already notified.
if (seen > 0)
return 0;
// if prctl was not seen, ignore the current syscall.
u64 prctl = __NR_prctl;
u64 *prctl_seen = seen_syscalls.lookup(&prctl);
if (prctl_seen == NULL) {
return 0;
}
}
data.stopTracing = false;

28
go.mod
View File

@ -1,12 +1,28 @@
module github.com/containers/oci-seccomp-bpf-hook
go 1.14
go 1.23.0
toolchain go1.23.8
require (
github.com/containers/common v0.48.0
github.com/containers/common v0.62.3
github.com/containers/storage v1.58.0
github.com/iovisor/gobpf v0.2.1-0.20221005153822-16120a1bf4d4
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1
github.com/opencontainers/runtime-spec v1.2.1
github.com/seccomp/libseccomp-golang v0.10.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
)
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
)

1493
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,6 @@ import (
"errors"
"flag"
"fmt"
"io/ioutil"
"log/syslog"
"os"
"os/exec"
@ -23,6 +22,7 @@ import (
"time"
types "github.com/containers/common/pkg/seccomp"
"github.com/containers/storage/pkg/unshare"
"github.com/iovisor/gobpf/bcc"
spec "github.com/opencontainers/runtime-spec/specs-go"
seccomp "github.com/seccomp/libseccomp-golang"
@ -57,6 +57,11 @@ func main() {
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")
outputFile := flag.String("o", "", "Path of the output file")
inputFile := flag.String("i", "", "Path of the input file")
@ -223,6 +228,13 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
m := bcc.NewModule(src, []string{})
defer m.Close()
table := bcc.NewTable(m.TableId("events"), m)
channel := make(chan []byte)
perfMap, err := bcc.InitPerfMap(table, channel, nil)
if err != nil {
return fmt.Errorf("error initializing perf map: %v", err)
}
logrus.Info("Loading enter tracepoint")
enterTrace, err := m.LoadTracepoint("enter_trace")
if err != nil {
@ -242,13 +254,6 @@ func runBPFSource(pid int, profilePath string, inputFile string) (finalErr error
return fmt.Errorf("error attaching to tracepoint: %v", err)
}
table := bcc.NewTable(m.TableId("events"), m)
channel := make(chan []byte)
perfMap, err := bcc.InitPerfMap(table, channel, nil)
if err != nil {
return fmt.Errorf("error initializing perf map: %v", err)
}
// Initialize the wait group used to wait for the tracing to be finished.
wg.Add(1)
var events []event
@ -322,7 +327,7 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
inputProfile := types.Seccomp{}
if inputFile != "" {
input, err := ioutil.ReadFile(inputFile)
input, err := os.ReadFile(inputFile)
if err != nil {
return fmt.Errorf("error reading input file: %v", err)
}
@ -359,7 +364,7 @@ func generateProfile(syscalls map[string]int, profilePath string, inputFile stri
if err != nil {
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 fmt.Errorf("error writing seccomp profile: %v", err)
}
return nil
@ -381,7 +386,7 @@ func parseAnnotation(annotation string) (outputFile string, inputFile string, er
return "", "", fmt.Errorf("%v: input file path must be absolute: %q", errInvalidAnnotation, inputFile)
}
inputProfile := types.Seccomp{}
input, err := ioutil.ReadFile(inputFile)
input, err := os.ReadFile(inputFile)
if err != nil {
return "", "", fmt.Errorf("%v: error reading input file: %q", errInvalidAnnotation, inputFile)
}

View File

@ -2,7 +2,6 @@ package main
import (
"encoding/json"
"io/ioutil"
"os"
"runtime"
"testing"
@ -15,7 +14,7 @@ func TestParseAnnotation(t *testing.T) {
testProfile := types.Seccomp{}
testProfile.DefaultAction = types.ActErrno
tmpFile, err := ioutil.TempFile(os.TempDir(), "input-*.json")
tmpFile, err := os.CreateTemp(os.TempDir(), "input-*.json")
if err != nil {
t.Fatalf("cannot create temporary file")
}

View File

@ -7,7 +7,6 @@ import (
"fmt"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
var (
@ -71,11 +70,12 @@ var (
// 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.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,
@ -107,7 +107,7 @@ func specToSeccomp(spec *specs.LinuxSeccomp) (*Seccomp, error) {
for _, arch := range spec.Architectures {
newArch, err := specArchToSeccompArch(arch)
if err != nil {
return nil, errors.Wrap(err, "convert spec arch")
return nil, fmt.Errorf("convert spec arch: %w", err)
}
res.Architectures = append(res.Architectures, newArch)
}
@ -115,7 +115,7 @@ func specToSeccomp(spec *specs.LinuxSeccomp) (*Seccomp, error) {
// Convert default action
newDefaultAction, err := specActionToSeccompAction(spec.DefaultAction)
if err != nil {
return nil, errors.Wrap(err, "convert default action")
return nil, fmt.Errorf("convert default action: %w", err)
}
res.DefaultAction = newDefaultAction
res.DefaultErrnoRet = spec.DefaultErrnoRet
@ -124,7 +124,7 @@ func specToSeccomp(spec *specs.LinuxSeccomp) (*Seccomp, error) {
for _, call := range spec.Syscalls {
newAction, err := specActionToSeccompAction(call.Action)
if err != nil {
return nil, errors.Wrap(err, "convert action")
return nil, fmt.Errorf("convert action: %w", err)
}
for _, name := range call.Names {
@ -139,7 +139,7 @@ func specToSeccomp(spec *specs.LinuxSeccomp) (*Seccomp, error) {
for _, arg := range call.Args {
newOp, err := specOperatorToSeccompOperator(arg.Op)
if err != nil {
return nil, errors.Wrap(err, "convert operator")
return nil, fmt.Errorf("convert operator: %w", err)
}
newArg := Arg{
@ -163,7 +163,7 @@ func specArchToLibseccompArch(arch specs.Arch) (string, error) {
if res, ok := specArchToLibseccompArchMap[arch]; ok {
return res, nil
}
return "", errors.Errorf(
return "", fmt.Errorf(
"architecture %q is not valid for libseccomp", arch,
)
}
@ -173,7 +173,7 @@ func specArchToSeccompArch(arch specs.Arch) (Arch, error) {
if res, ok := specArchToSeccompArchMap[arch]; ok {
return res, nil
}
return "", errors.Errorf("architecture %q is not valid", arch)
return "", fmt.Errorf("architecture %q is not valid", arch)
}
// specActionToSeccompAction converts a spec action into a seccomp one.
@ -181,7 +181,7 @@ func specActionToSeccompAction(action specs.LinuxSeccompAction) (Action, error)
if res, ok := specActionToSeccompActionMap[action]; ok {
return res, nil
}
return "", errors.Errorf(
return "", fmt.Errorf(
"spec action %q is not valid internal action", action,
)
}
@ -191,7 +191,7 @@ func specOperatorToSeccompOperator(operator specs.LinuxSeccompOperator) (Operato
if op, ok := specOperatorToSeccompOperatorMap[operator]; ok {
return op, nil
}
return "", errors.Errorf(
return "", fmt.Errorf(
"spec operator %q is not a valid internal operator", operator,
)
}

View File

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

View File

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

View File

@ -1,5 +1,4 @@
//go:build seccomp
// +build seccomp
// NOTE: this package has originally been copied from
// github.com/opencontainers/runc and modified to work for other use cases
@ -7,8 +6,10 @@
package seccomp
import (
"errors"
"fmt"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
libseccomp "github.com/seccomp/libseccomp-golang"
"golang.org/x/sys/unix"
)
@ -39,39 +40,39 @@ func BuildFilter(spec *specs.LinuxSeccomp) (*libseccomp.ScmpFilter, error) {
profile, err := specToSeccomp(spec)
if err != nil {
return nil, errors.Wrap(err, "convert spec to seccomp profile")
return nil, fmt.Errorf("convert spec to seccomp profile: %w", err)
}
defaultAction, err := toAction(profile.DefaultAction, profile.DefaultErrnoRet)
if err != nil {
return nil, errors.Wrapf(err, "convert default action %s", profile.DefaultAction)
return nil, fmt.Errorf("convert default action %s: %w", profile.DefaultAction, err)
}
filter, err := libseccomp.NewFilter(defaultAction)
if err != nil {
return nil, errors.Wrapf(err, "create filter for default action %s", defaultAction)
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, errors.Wrap(err, "convert spec arch")
return nil, fmt.Errorf("convert spec arch: %w", err)
}
scmpArch, err := libseccomp.GetArchFromString(libseccompArch)
if err != nil {
return nil, errors.Wrapf(err, "validate Seccomp architecture %s", arch)
return nil, fmt.Errorf("validate Seccomp architecture %s: %w", arch, err)
}
if err := filter.AddArch(scmpArch); err != nil {
return nil, errors.Wrap(err, "add architecture to seccomp filter")
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, errors.Wrap(err, "set no new privileges flag")
return nil, fmt.Errorf("set no new privileges flag: %w", err)
}
// Add a rule for each syscall
@ -81,7 +82,7 @@ func BuildFilter(spec *specs.LinuxSeccomp) (*libseccomp.ScmpFilter, error) {
}
if err = matchSyscall(filter, call); err != nil {
return nil, errors.Wrap(err, "filter matches syscall")
return nil, fmt.Errorf("filter matches syscall: %w", err)
}
}
@ -107,13 +108,13 @@ func matchSyscall(filter *libseccomp.ScmpFilter, call *Syscall) error {
// Convert the call's action to the libseccomp equivalent
callAct, err := toAction(call.Action, call.ErrnoRet)
if err != nil {
return errors.Wrapf(err, "convert action %s", call.Action)
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 errors.Wrapf(err, "add seccomp filter rule for syscall %s", call.Name)
return fmt.Errorf("add seccomp filter rule for syscall %s: %w", call.Name, err)
}
} else {
// Linux system calls can have at most 6 arguments
@ -127,10 +128,10 @@ func matchSyscall(filter *libseccomp.ScmpFilter, call *Syscall) error {
for _, cond := range call.Args {
newCond, err := toCondition(cond)
if err != nil {
return errors.Wrapf(err, "create seccomp syscall condition for syscall %s", call.Name)
return fmt.Errorf("create seccomp syscall condition for syscall %s: %w", call.Name, err)
}
argCounts[cond.Index] += 1
argCounts[cond.Index]++
conditions = append(conditions, newCond)
}
@ -150,13 +151,13 @@ func matchSyscall(filter *libseccomp.ScmpFilter, call *Syscall) error {
condArr := []libseccomp.ScmpCondition{cond}
if err = filter.AddRuleConditional(callNum, callAct, condArr); err != nil {
return errors.Wrapf(err, "add seccomp rule for syscall %s", call.Name)
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 errors.Wrapf(err, "add seccomp rule for syscall %s", call.Name)
return fmt.Errorf("add seccomp rule for syscall %s: %w", call.Name, err)
}
}
@ -168,7 +169,8 @@ func matchSyscall(filter *libseccomp.ScmpFilter, call *Syscall) error {
func toAction(act Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
switch act {
case ActKill:
return libseccomp.ActKill, nil
// lint was not passing until this was changed from ActKill to ActKilThread.
return libseccomp.ActKillThread, nil
case ActKillProcess:
return libseccomp.ActKillProcess, nil
case ActErrno:
@ -188,7 +190,7 @@ func toAction(act Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
case ActLog:
return libseccomp.ActLog, nil
default:
return libseccomp.ActInvalid, errors.Errorf("invalid action %s", act)
return libseccomp.ActInvalid, fmt.Errorf("invalid action %s", act)
}
}
@ -201,14 +203,14 @@ func toCondition(arg *Arg) (cond libseccomp.ScmpCondition, err error) {
op, err := toCompareOp(arg.Op)
if err != nil {
return cond, errors.Wrap(err, "convert compare operator")
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, errors.Wrap(err, "make condition")
return cond, fmt.Errorf("make condition: %w", err)
}
return condition, nil
@ -233,6 +235,6 @@ func toCompareOp(op Operator) (libseccomp.ScmpCompareOp, error) {
case OpMaskedEqual:
return libseccomp.CompareMaskedEqual, nil
default:
return libseccomp.CompareInvalid, errors.Errorf("invalid operator %s", op)
return libseccomp.CompareInvalid, fmt.Errorf("invalid operator %s", op)
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -75,6 +75,7 @@ const (
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

View File

@ -1,12 +1,10 @@
//go:build seccomp
// +build seccomp
package seccomp
import (
"encoding/json"
"github.com/pkg/errors"
"fmt"
)
// ValidateProfile does a basic validation for the provided seccomp profile
@ -14,16 +12,16 @@ import (
func ValidateProfile(content string) error {
profile := &Seccomp{}
if err := json.Unmarshal([]byte(content), &profile); err != nil {
return errors.Wrap(err, "decoding seccomp profile")
return fmt.Errorf("decoding seccomp profile: %w", err)
}
spec, err := setupSeccomp(profile, nil)
if err != nil {
return errors.Wrap(err, "create seccomp spec")
return fmt.Errorf("create seccomp spec: %w", err)
}
if _, err := BuildFilter(spec); err != nil {
return errors.Wrap(err, "build seccomp filter")
return fmt.Errorf("build seccomp filter: %w", err)
}
return nil

1523
vendor/github.com/containers/storage/AUTHORS generated vendored Normal file

File diff suppressed because it is too large Load Diff

191
vendor/github.com/containers/storage/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2013-2016 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

19
vendor/github.com/containers/storage/NOTICE generated vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

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

View File

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

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

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

View File

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

149
vendor/github.com/containers/storage/pkg/mount/flags.go generated vendored Normal file
View File

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

View File

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

View File

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

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

110
vendor/github.com/containers/storage/pkg/mount/mount.go generated vendored Normal file
View File

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

View File

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

View File

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

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

View File

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

View File

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

View 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), "")
}

View File

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

View File

@ -0,0 +1,7 @@
//go:build windows
package mount
func unmount(target string, flag int) error {
panic("Not implemented")
}

View File

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

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

View File

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

View File

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

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

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

View File

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

View File

@ -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 := &regexpStruct{
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() {}

View File

@ -0,0 +1,5 @@
//go:build !regexp_precompile
package regexp
const precompile = false

View File

@ -0,0 +1,5 @@
//go:build regexp_precompile
package regexp
const precompile = true

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@
//go:build !windows
package system
// LCOWSupported returns true if Linux containers on Windows are supported.
func LCOWSupported() bool {
return false
}

View File

@ -0,0 +1,6 @@
package system
// LCOWSupported returns true if Linux containers on Windows are supported.
func LCOWSupported() bool {
return lcowSupported
}

View File

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

View File

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

View File

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

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

99
vendor/github.com/containers/storage/pkg/system/rm.go generated vendored Normal file
View File

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

View File

@ -0,0 +1,9 @@
//go:build !freebsd
package system
// Reset file flags in a directory tree. This allows EnsureRemoveAll
// to delete trees which have the immutable flag set.
func resetFileFlags(dir string) error {
return nil
}

View File

@ -0,0 +1,17 @@
package system
import (
"io/fs"
"path/filepath"
)
// Reset file flags in a directory tree. This allows EnsureRemoveAll
// to delete trees which have the immutable flag set.
func resetFileFlags(dir string) error {
return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err := Lchflags(path, 0); err != nil {
return err
}
return nil
})
}

View File

@ -0,0 +1,11 @@
//go:build !freebsd
package system
type platformStatT struct{}
// Flags return file flags if supported or zero otherwise
func (s StatT) Flags() uint32 {
_ = s.platformStatT // Silence warnings that StatT.platformStatT is unused (on these platforms)
return 0
}

View File

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

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