Import Upstream version 0.9.0
This commit is contained in:
parent
c8f7fdb963
commit
015eefdf88
|
@ -3,4 +3,3 @@ indent_style = space
|
|||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
indent_brace_style = gnu
|
||||
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
name: CI checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Build with Autotools and gcc, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v1
|
||||
- name: Install build-dependencies
|
||||
run: sudo ./ci/builddeps.sh
|
||||
- name: Create logs dir
|
||||
run: mkdir test-logs
|
||||
- name: autogen.sh
|
||||
run: NOCONFIGURE=1 ./autogen.sh
|
||||
- name: configure
|
||||
run: |
|
||||
mkdir _build
|
||||
pushd _build
|
||||
../configure \
|
||||
--enable-man \
|
||||
--enable-selinux \
|
||||
${NULL+}
|
||||
popd
|
||||
env:
|
||||
CFLAGS: >-
|
||||
-O2
|
||||
-Wp,-D_FORTIFY_SOURCE=2
|
||||
-fsanitize=address
|
||||
-fsanitize=undefined
|
||||
- name: make
|
||||
run: make -C _build -j $(getconf _NPROCESSORS_ONLN) V=1
|
||||
- name: smoke-test
|
||||
run: |
|
||||
set -x
|
||||
./_build/bwrap --bind / / --tmpfs /tmp true
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
- name: check
|
||||
run: |
|
||||
make -C _build -j $(getconf _NPROCESSORS_ONLN) check VERBOSE=1 BWRAP_MUST_WORK=1
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
- name: Collect overall test logs on failure
|
||||
if: failure()
|
||||
run: mv _build/test-suite.log test-logs/ || true
|
||||
- name: Collect individual test logs on cancel
|
||||
if: failure() || cancelled()
|
||||
run: mv _build/tests/*.log test-logs/ || true
|
||||
- name: Upload test logs
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure() || cancelled()
|
||||
with:
|
||||
name: test logs
|
||||
path: test-logs
|
||||
- name: install
|
||||
run: |
|
||||
make -C _build install DESTDIR="$(pwd)/DESTDIR"
|
||||
( cd DESTDIR && find -ls )
|
||||
- name: distcheck
|
||||
run: |
|
||||
make -C _build -j $(getconf _NPROCESSORS_ONLN) distcheck VERBOSE=1 BWRAP_MUST_WORK=1
|
||||
|
||||
meson:
|
||||
name: Build with Meson and gcc, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v1
|
||||
- name: Install build-dependencies
|
||||
run: sudo ./ci/builddeps.sh
|
||||
- name: Create logs dir
|
||||
run: mkdir test-logs
|
||||
- name: setup
|
||||
run: |
|
||||
meson _build
|
||||
env:
|
||||
CFLAGS: >-
|
||||
-O2
|
||||
-Wp,-D_FORTIFY_SOURCE=2
|
||||
-fsanitize=address
|
||||
-fsanitize=undefined
|
||||
- name: compile
|
||||
run: ninja -C _build -v
|
||||
- name: smoke-test
|
||||
run: |
|
||||
set -x
|
||||
./_build/bwrap --bind / / --tmpfs /tmp true
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
- name: test
|
||||
run: |
|
||||
BWRAP_MUST_WORK=1 meson test -C _build
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
- name: Collect overall test logs on failure
|
||||
if: failure()
|
||||
run: mv _build/meson-logs/testlog.txt test-logs/ || true
|
||||
- name: install
|
||||
run: |
|
||||
DESTDIR="$(pwd)/DESTDIR" meson install -C _build
|
||||
( cd DESTDIR && find -ls )
|
||||
- name: dist
|
||||
run: |
|
||||
BWRAP_MUST_WORK=1 meson dist -C _build
|
||||
- name: Collect dist test logs on failure
|
||||
if: failure()
|
||||
run: mv _build/meson-private/dist-build/meson-logs/testlog.txt test-logs/disttestlog.txt || true
|
||||
- name: use as subproject
|
||||
run: |
|
||||
mkdir tests/use-as-subproject/subprojects
|
||||
tar -C tests/use-as-subproject/subprojects -xf _build/meson-dist/bubblewrap-*.tar.xz
|
||||
mv tests/use-as-subproject/subprojects/bubblewrap-* tests/use-as-subproject/subprojects/bubblewrap
|
||||
( cd tests/use-as-subproject && meson _build )
|
||||
ninja -C tests/use-as-subproject/_build -v
|
||||
meson test -C tests/use-as-subproject/_build
|
||||
DESTDIR="$(pwd)/DESTDIR-as-subproject" meson install -C tests/use-as-subproject/_build
|
||||
( cd DESTDIR-as-subproject && find -ls )
|
||||
test -x DESTDIR-as-subproject/usr/local/libexec/not-flatpak-bwrap
|
||||
test ! -e DESTDIR-as-subproject/usr/local/bin/bwrap
|
||||
test ! -e DESTDIR-as-subproject/usr/local/libexec/bwrap
|
||||
tests/use-as-subproject/assert-correct-rpath.py DESTDIR-as-subproject/usr/local/libexec/not-flatpak-bwrap
|
||||
- name: Upload test logs
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure() || cancelled()
|
||||
with:
|
||||
name: test logs
|
||||
path: test-logs
|
||||
|
||||
clang:
|
||||
name: Build with clang and analyze
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language:
|
||||
- cpp
|
||||
steps:
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Check out
|
||||
uses: actions/checkout@v1
|
||||
- name: Install build-dependencies
|
||||
run: sudo ./ci/builddeps.sh --clang
|
||||
- name: autogen.sh
|
||||
run: NOCONFIGURE=1 ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --enable-selinux
|
||||
env:
|
||||
CC: clang
|
||||
CFLAGS: >-
|
||||
-O2
|
||||
-Werror=unused-variable
|
||||
- name: make
|
||||
run: make -j $(getconf _NPROCESSORS_ONLN) V=1
|
||||
- name: CodeQL analysis
|
||||
uses: github/codeql-action/analyze@v2
|
|
@ -0,0 +1,3 @@
|
|||
## The bubblewrap Project Community Code of Conduct
|
||||
|
||||
The bubblewrap project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/HEAD/CODE-OF-CONDUCT.md).
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
bwrap_SOURCES = \
|
||||
$(bwrap_srcpath)/bubblewrap.c \
|
||||
$(bwrap_srcpath)/bind-mount.h \
|
||||
|
|
51
Makefile.am
51
Makefile.am
|
@ -5,11 +5,21 @@ EXTRA_DIST = \
|
|||
.editorconfig \
|
||||
README.md \
|
||||
autogen.sh \
|
||||
completions/bash/meson.build \
|
||||
completions/meson.build \
|
||||
completions/zsh/meson.build \
|
||||
demos/bubblewrap-shell.sh \
|
||||
demos/flatpak-run.sh \
|
||||
demos/flatpak.bpf \
|
||||
demos/userns-block-fd.py \
|
||||
meson.build \
|
||||
meson_options.txt \
|
||||
packaging/bubblewrap.spec \
|
||||
tests/meson.build \
|
||||
tests/use-as-subproject/README \
|
||||
tests/use-as-subproject/config.h \
|
||||
tests/use-as-subproject/dummy-config.h.in \
|
||||
tests/use-as-subproject/meson.build \
|
||||
uncrustify.cfg \
|
||||
uncrustify.sh \
|
||||
$(NULL)
|
||||
|
@ -27,33 +37,64 @@ if PRIV_MODE_SETUID
|
|||
$(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap
|
||||
endif
|
||||
|
||||
check_PROGRAMS = test-bwrap
|
||||
test_programs = \
|
||||
tests/test-utils \
|
||||
$(NULL)
|
||||
test_scripts = \
|
||||
tests/test-run.sh \
|
||||
tests/test-seccomp.py \
|
||||
tests/test-specifying-userns.sh \
|
||||
tests/test-specifying-pidns.sh \
|
||||
$(NULL)
|
||||
test_extra_programs = \
|
||||
test-bwrap \
|
||||
tests/try-syscall \
|
||||
$(NULL)
|
||||
|
||||
test-bwrap: bwrap
|
||||
test-bwrap$(EXEEXT): bwrap
|
||||
rm -rf test-bwrap
|
||||
cp bwrap test-bwrap
|
||||
chmod 0755 test-bwrap
|
||||
if PRIV_MODE_SETUID
|
||||
$(SUDO_BIN) chown root test-bwrap
|
||||
$(SUDO_BIN) chmod u+s test-bwrap
|
||||
endif
|
||||
|
||||
tests_test_utils_SOURCES = \
|
||||
tests/test-utils.c \
|
||||
utils.h \
|
||||
utils.c \
|
||||
$(NULL)
|
||||
tests_test_utils_LDADD = $(SELINUX_LIBS)
|
||||
|
||||
test_bwrap_SOURCES=
|
||||
|
||||
include Makefile-docs.am
|
||||
|
||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
|
||||
LOG_COMPILER =
|
||||
TESTS = tests/test-run.sh
|
||||
TESTS_ENVIRONMENT = BWRAP=$(abs_top_builddir)/test-bwrap
|
||||
TESTS_ENVIRONMENT = \
|
||||
BWRAP=$(abs_top_builddir)/test-bwrap \
|
||||
G_TEST_BUILDDIR=$(abs_top_builddir) \
|
||||
G_TEST_SRCDIR=$(abs_top_srcdir) \
|
||||
$(NULL)
|
||||
check_PROGRAMS = $(test_programs) $(test_extra_programs)
|
||||
TESTS = $(test_programs) $(test_scripts)
|
||||
|
||||
EXTRA_DIST += $(TESTS)
|
||||
EXTRA_DIST += $(test_scripts)
|
||||
EXTRA_DIST += tests/libtest-core.sh
|
||||
EXTRA_DIST += tests/libtest.sh
|
||||
|
||||
if ENABLE_BASH_COMPLETION
|
||||
bashcompletiondir = $(BASH_COMPLETION_DIR)
|
||||
dist_bashcompletion_DATA = completions/bash/bwrap
|
||||
endif
|
||||
|
||||
if ENABLE_ZSH_COMPLETION
|
||||
zshcompletiondir = $(ZSH_COMPLETION_DIR)
|
||||
dist_zshcompletion_DATA = completions/zsh/_bwrap
|
||||
endif
|
||||
|
||||
-include $(top_srcdir)/git.mk
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
|
|
1370
Makefile.in
1370
Makefile.in
File diff suppressed because it is too large
Load Diff
97
README.md
97
README.md
|
@ -6,7 +6,7 @@ etc. focus on providing infrastructure for system administrators and
|
|||
orchestration tools (e.g. Kubernetes) to run containers.
|
||||
|
||||
These tools are not suitable to give to unprivileged users, because it
|
||||
is trivial to turn such access into to a fully privileged root shell
|
||||
is trivial to turn such access into a fully privileged root shell
|
||||
on the host.
|
||||
|
||||
User namespaces
|
||||
|
@ -31,12 +31,12 @@ user namespaces. Emphasis on subset - specifically relevant to the
|
|||
above CVE, bubblewrap does not allow control over iptables.
|
||||
|
||||
The original bubblewrap code existed before user namespaces - it inherits code from
|
||||
[xdg-app helper](https://cgit.freedesktop.org/xdg-app/xdg-app/tree/common/xdg-app-helper.c)
|
||||
[xdg-app helper](https://cgit.freedesktop.org/xdg-app/xdg-app/tree/common/xdg-app-helper.c?id=4c3bf179e2e4a2a298cd1db1d045adaf3f564532)
|
||||
which in turn distantly derives from
|
||||
[linux-user-chroot](https://git.gnome.org/browse/linux-user-chroot).
|
||||
|
||||
Security
|
||||
--------
|
||||
System security
|
||||
---------------
|
||||
|
||||
The maintainers of this tool believe that it does not, even when used
|
||||
in combination with typical software installed on that distribution,
|
||||
|
@ -47,6 +47,30 @@ In particular, bubblewrap uses `PR_SET_NO_NEW_PRIVS` to turn off
|
|||
setuid binaries, which is the [traditional way](https://en.wikipedia.org/wiki/Chroot#Limitations) to get out of things
|
||||
like chroots.
|
||||
|
||||
Sandbox security
|
||||
----------------
|
||||
|
||||
bubblewrap is a tool for constructing sandbox environments.
|
||||
bubblewrap is not a complete, ready-made sandbox with a specific security
|
||||
policy.
|
||||
|
||||
Some of bubblewrap's use-cases want a security boundary between the sandbox
|
||||
and the real system; other use-cases want the ability to change the layout of
|
||||
the filesystem for processes inside the sandbox, but do not aim to be a
|
||||
security boundary.
|
||||
As a result, the level of protection between the sandboxed processes and
|
||||
the host system is entirely determined by the arguments passed to
|
||||
bubblewrap.
|
||||
|
||||
Whatever program constructs the command-line arguments for bubblewrap
|
||||
(often a larger framework like Flatpak, libgnome-desktop, sandwine
|
||||
or an ad-hoc script) is responsible for defining its own security model,
|
||||
and choosing appropriate bubblewrap command-line arguments to implement
|
||||
that security model.
|
||||
|
||||
Some aspects of sandbox security that require particular care are described
|
||||
in the [Limitations](#limitations) section below.
|
||||
|
||||
Users
|
||||
-----
|
||||
|
||||
|
@ -62,6 +86,31 @@ clusters. Having the ability for unprivileged users to use container
|
|||
features would make it significantly easier to do interactive
|
||||
debugging scenarios and the like.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
bubblewrap is available in the package repositories of the most Linux distributions
|
||||
and can be installed from there.
|
||||
|
||||
If you need to build bubblewrap from source, you can do this with meson or autotools.
|
||||
|
||||
meson:
|
||||
|
||||
```
|
||||
meson _builddir
|
||||
meson compile -C _builddir
|
||||
meson test -C _builddir
|
||||
meson install -C _builddir
|
||||
```
|
||||
|
||||
autotools:
|
||||
|
||||
```
|
||||
./autogen.sh
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
|
@ -77,7 +126,14 @@ source code, but here's a trimmed down version which runs
|
|||
a new shell reusing the host's `/usr`.
|
||||
|
||||
```
|
||||
bwrap --ro-bind /usr /usr --symlink usr/lib64 /lib64 --proc /proc --dev /dev --unshare-pid bash
|
||||
bwrap \
|
||||
--ro-bind /usr /usr \
|
||||
--symlink usr/lib64 /lib64 \
|
||||
--proc /proc \
|
||||
--dev /dev \
|
||||
--unshare-pid \
|
||||
--new-session \
|
||||
bash
|
||||
```
|
||||
|
||||
This is an incomplete example, but useful for purposes of
|
||||
|
@ -114,10 +170,39 @@ UTS namespace ([CLONE_NEWUTS](http://linux.die.net/man/2/clone)): The sandbox wi
|
|||
|
||||
Seccomp filters: You can pass in seccomp filters that limit which syscalls can be done in the sandbox. For more information, see [Seccomp](https://en.wikipedia.org/wiki/Seccomp).
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
As noted in the [Sandbox security](#sandbox-security) section above,
|
||||
the level of protection between the sandboxed processes and the host system
|
||||
is entirely determined by the arguments passed to bubblewrap.
|
||||
Some aspects that require special care are noted here.
|
||||
|
||||
- If you are not filtering out `TIOCSTI` commands using seccomp filters,
|
||||
argument `--new-session` is needed to protect against out-of-sandbox
|
||||
command execution
|
||||
(see [CVE-2017-5226](https://github.com/containers/bubblewrap/issues/142)).
|
||||
|
||||
- Everything mounted into the sandbox can potentially be used to escalate
|
||||
privileges.
|
||||
For example, if you bind a D-Bus socket into the sandbox, it can be used to
|
||||
execute commands via systemd. You can use
|
||||
[xdg-dbus-proxy](https://github.com/flatpak/xdg-dbus-proxy) to filter
|
||||
D-Bus communication.
|
||||
|
||||
- Some applications deploy their own sandboxing mechanisms, and these can be
|
||||
restricted by the constraints imposed by bubblewrap's sandboxing.
|
||||
For example, some web browsers which configure their child proccesses via
|
||||
seccomp to not have access to the filesystem. If you limit the syscalls and
|
||||
don't allow the seccomp syscall, a browser cannot apply these restrictions.
|
||||
Similarly, if these rules were compiled into a file that is not available in
|
||||
the sandbox, the browser cannot load these rules from this file and cannot
|
||||
apply these restrictions.
|
||||
|
||||
Related project comparison: Firejail
|
||||
------------------------------------
|
||||
|
||||
[Firejail](https://github.com/netblue30/firejail/tree/master/src/firejail)
|
||||
[Firejail](https://github.com/netblue30/firejail/tree/HEAD/src/firejail)
|
||||
is similar to Flatpak before bubblewrap was split out in that it combines
|
||||
a setuid tool with a lot of desktop-specific sandboxing features. For
|
||||
example, Firejail knows about Pulseaudio, whereas bubblewrap does not.
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
## Security and Disclosure Information Policy for the bubblewrap Project
|
||||
|
||||
The bubblewrap Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/HEAD/SECURITY.md) for the Containers Projects.
|
||||
|
||||
### System security
|
||||
|
||||
If bubblewrap is setuid root, then the goal is that it does not allow
|
||||
a malicious local user to do anything that would not have been possible
|
||||
on a kernel that allows unprivileged users to create new user namespaces.
|
||||
For example, [CVE-2020-5291](https://github.com/containers/bubblewrap/security/advisories/GHSA-j2qp-rvxj-43vj)
|
||||
was treated as a security vulnerability in bubblewrap.
|
||||
|
||||
If bubblewrap is not setuid root, then it is not a security boundary
|
||||
between the user and the OS, because anything bubblewrap could do, a
|
||||
malicious user could equally well do by writing their own tool equivalent
|
||||
to bubblewrap.
|
||||
|
||||
### Sandbox security
|
||||
|
||||
bubblewrap is a toolkit for constructing sandbox environments.
|
||||
bubblewrap is not a complete, ready-made sandbox with a specific security
|
||||
policy.
|
||||
|
||||
Some of bubblewrap's use-cases want a security boundary between the sandbox
|
||||
and the real system; other use-cases want the ability to change the layout of
|
||||
the filesystem for processes inside the sandbox, but do not aim to be a
|
||||
security boundary.
|
||||
As a result, the level of protection between the sandboxed processes and
|
||||
the host system is entirely determined by the arguments passed to
|
||||
bubblewrap.
|
||||
|
||||
Whatever program constructs the command-line arguments for bubblewrap
|
||||
(often a larger framework like Flatpak, libgnome-desktop, sandwine
|
||||
or an ad-hoc script) is responsible for defining its own security model,
|
||||
and choosing appropriate bubblewrap command-line arguments to implement
|
||||
that security model.
|
||||
|
||||
For example,
|
||||
[CVE-2017-5226](https://github.com/flatpak/flatpak/security/advisories/GHSA-7gfv-rvfx-h87x)
|
||||
(in which a Flatpak app could send input to a parent terminal using the
|
||||
`TIOCSTI` ioctl) is considered to be a Flatpak vulnerability, not a
|
||||
bubblewrap vulnerability.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
test -n "$srcdir" || srcdir=`dirname "$0"`
|
||||
test -n "$srcdir" || srcdir=$(dirname "$0")
|
||||
test -n "$srcdir" || srcdir=.
|
||||
|
||||
olddir=`pwd`
|
||||
cd $srcdir
|
||||
olddir=$(pwd)
|
||||
cd "$srcdir"
|
||||
|
||||
if ! (autoreconf --version >/dev/null 2>&1); then
|
||||
echo "*** No autoreconf found, please install it ***"
|
||||
|
@ -15,5 +15,5 @@ mkdir -p m4
|
|||
|
||||
autoreconf --force --install --verbose
|
||||
|
||||
cd $olddir
|
||||
cd "$olddir"
|
||||
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
|
||||
|
|
197
bind-mount.c
197
bind-mount.c
|
@ -1,5 +1,6 @@
|
|||
/* bubblewrap
|
||||
* Copyright (C) 2016 Alexander Larsson
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -85,7 +86,7 @@ decode_mountoptions (const char *options)
|
|||
int i;
|
||||
unsigned long flags = 0;
|
||||
static const struct { int flag;
|
||||
char *name;
|
||||
const char *name;
|
||||
} flags_data[] = {
|
||||
{ 0, "rw" },
|
||||
{ MS_RDONLY, "ro" },
|
||||
|
@ -236,7 +237,7 @@ parse_mountinfo (int proc_fd,
|
|||
MountInfo *end_tab;
|
||||
int n_mounts;
|
||||
char *line;
|
||||
int i;
|
||||
unsigned int i;
|
||||
int max_id;
|
||||
unsigned int n_lines;
|
||||
int root;
|
||||
|
@ -246,7 +247,7 @@ parse_mountinfo (int proc_fd,
|
|||
die_with_error ("Can't open /proc/self/mountinfo");
|
||||
|
||||
n_lines = count_lines (mountinfo);
|
||||
lines = xcalloc (n_lines * sizeof (MountInfoLine));
|
||||
lines = xcalloc (n_lines, sizeof (MountInfoLine));
|
||||
|
||||
max_id = 0;
|
||||
line = mountinfo;
|
||||
|
@ -309,11 +310,11 @@ parse_mountinfo (int proc_fd,
|
|||
|
||||
if (root == -1)
|
||||
{
|
||||
mount_tab = xcalloc (sizeof (MountInfo) * (1));
|
||||
mount_tab = xcalloc (1, sizeof (MountInfo));
|
||||
return steal_pointer (&mount_tab);
|
||||
}
|
||||
|
||||
by_id = xcalloc ((max_id + 1) * sizeof (MountInfoLine*));
|
||||
by_id = xcalloc (max_id + 1, sizeof (MountInfoLine*));
|
||||
for (i = 0; i < n_lines; i++)
|
||||
by_id[lines[i].id] = &lines[i];
|
||||
|
||||
|
@ -365,7 +366,7 @@ parse_mountinfo (int proc_fd,
|
|||
}
|
||||
|
||||
n_mounts = count_mounts (&lines[root]);
|
||||
mount_tab = xcalloc (sizeof (MountInfo) * (n_mounts + 1));
|
||||
mount_tab = xcalloc (n_mounts + 1, sizeof (MountInfo));
|
||||
|
||||
end_tab = collect_mounts (&mount_tab[0], &lines[root]);
|
||||
assert (end_tab == &mount_tab[n_mounts]);
|
||||
|
@ -373,11 +374,12 @@ parse_mountinfo (int proc_fd,
|
|||
return steal_pointer (&mount_tab);
|
||||
}
|
||||
|
||||
int
|
||||
bind_mount_result
|
||||
bind_mount (int proc_fd,
|
||||
const char *src,
|
||||
const char *dest,
|
||||
bind_option_t options)
|
||||
bind_option_t options,
|
||||
char **failing_path)
|
||||
{
|
||||
bool readonly = (options & BIND_READONLY) != 0;
|
||||
bool devices = (options & BIND_DEVICES) != 0;
|
||||
|
@ -385,34 +387,76 @@ bind_mount (int proc_fd,
|
|||
unsigned long current_flags, new_flags;
|
||||
cleanup_mount_tab MountTab mount_tab = NULL;
|
||||
cleanup_free char *resolved_dest = NULL;
|
||||
cleanup_free char *dest_proc = NULL;
|
||||
cleanup_free char *oldroot_dest_proc = NULL;
|
||||
cleanup_free char *kernel_case_combination = NULL;
|
||||
cleanup_fd int dest_fd = -1;
|
||||
int i;
|
||||
|
||||
if (src)
|
||||
{
|
||||
if (mount (src, dest, NULL, MS_BIND | (recursive ? MS_REC : 0), NULL) != 0)
|
||||
return 1;
|
||||
if (mount (src, dest, NULL, MS_SILENT | MS_BIND | (recursive ? MS_REC : 0), NULL) != 0)
|
||||
return BIND_MOUNT_ERROR_MOUNT;
|
||||
}
|
||||
|
||||
/* The mount operation will resolve any symlinks in the destination
|
||||
path, so to find it in the mount table we need to do that too. */
|
||||
resolved_dest = realpath (dest, NULL);
|
||||
if (resolved_dest == NULL)
|
||||
return 2;
|
||||
return BIND_MOUNT_ERROR_REALPATH_DEST;
|
||||
|
||||
mount_tab = parse_mountinfo (proc_fd, resolved_dest);
|
||||
if (mount_tab[0].mountpoint == NULL)
|
||||
dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC);
|
||||
if (dest_fd < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 2; /* No mountpoint at dest */
|
||||
if (failing_path != NULL)
|
||||
*failing_path = steal_pointer (&resolved_dest);
|
||||
|
||||
return BIND_MOUNT_ERROR_REOPEN_DEST;
|
||||
}
|
||||
|
||||
assert (path_equal (mount_tab[0].mountpoint, resolved_dest));
|
||||
/* If we are in a case-insensitive filesystem, mountinfo might contain a
|
||||
* different case combination of the path we requested to mount.
|
||||
* This is due to the fact that the kernel, as of the beginning of 2021,
|
||||
* populates mountinfo with whatever case combination first appeared in the
|
||||
* dcache; kernel developers plan to change this in future so that it
|
||||
* reflects the on-disk encoding instead.
|
||||
* To avoid throwing an error when this happens, we use readlink() result
|
||||
* instead of the provided @root_mount, so that we can compare the mountinfo
|
||||
* entries with the same case combination that the kernel is expected to
|
||||
* use. */
|
||||
dest_proc = xasprintf ("/proc/self/fd/%d", dest_fd);
|
||||
oldroot_dest_proc = get_oldroot_path (dest_proc);
|
||||
kernel_case_combination = readlink_malloc (oldroot_dest_proc);
|
||||
if (kernel_case_combination == NULL)
|
||||
{
|
||||
if (failing_path != NULL)
|
||||
*failing_path = steal_pointer (&resolved_dest);
|
||||
|
||||
return BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD;
|
||||
}
|
||||
|
||||
mount_tab = parse_mountinfo (proc_fd, kernel_case_combination);
|
||||
if (mount_tab[0].mountpoint == NULL)
|
||||
{
|
||||
if (failing_path != NULL)
|
||||
*failing_path = steal_pointer (&kernel_case_combination);
|
||||
|
||||
errno = EINVAL;
|
||||
return BIND_MOUNT_ERROR_FIND_DEST_MOUNT;
|
||||
}
|
||||
|
||||
assert (path_equal (mount_tab[0].mountpoint, kernel_case_combination));
|
||||
current_flags = mount_tab[0].options;
|
||||
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
|
||||
if (new_flags != current_flags &&
|
||||
mount ("none", resolved_dest,
|
||||
NULL, MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
|
||||
return 3;
|
||||
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
|
||||
{
|
||||
if (failing_path != NULL)
|
||||
*failing_path = steal_pointer (&resolved_dest);
|
||||
|
||||
return BIND_MOUNT_ERROR_REMOUNT_DEST;
|
||||
}
|
||||
|
||||
/* We need to work around the fact that a bind mount does not apply the flags, so we need to manually
|
||||
* apply the flags to all submounts in the recursive case.
|
||||
|
@ -426,15 +470,126 @@ bind_mount (int proc_fd,
|
|||
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
|
||||
if (new_flags != current_flags &&
|
||||
mount ("none", mount_tab[i].mountpoint,
|
||||
NULL, MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
|
||||
NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
|
||||
{
|
||||
/* If we can't read the mountpoint we can't remount it, but that should
|
||||
be safe to ignore because its not something the user can access. */
|
||||
if (errno != EACCES)
|
||||
return 5;
|
||||
{
|
||||
if (failing_path != NULL)
|
||||
*failing_path = xstrdup (mount_tab[i].mountpoint);
|
||||
|
||||
return BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return BIND_MOUNT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representing bind_mount_result, like strerror().
|
||||
* If want_errno_p is non-NULL, *want_errno_p is used to indicate whether
|
||||
* it would make sense to print strerror(saved_errno).
|
||||
*/
|
||||
static char *
|
||||
bind_mount_result_to_string (bind_mount_result res,
|
||||
const char *failing_path,
|
||||
bool *want_errno_p)
|
||||
{
|
||||
char *string = NULL;
|
||||
bool want_errno = TRUE;
|
||||
|
||||
switch (res)
|
||||
{
|
||||
case BIND_MOUNT_ERROR_MOUNT:
|
||||
string = xstrdup ("Unable to mount source on destination");
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_ERROR_REALPATH_DEST:
|
||||
string = xstrdup ("realpath(destination)");
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_ERROR_REOPEN_DEST:
|
||||
string = xasprintf ("open(\"%s\", O_PATH)", failing_path);
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD:
|
||||
string = xasprintf ("readlink(/proc/self/fd/N) for \"%s\"", failing_path);
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
|
||||
string = xasprintf ("Unable to find \"%s\" in mount table", failing_path);
|
||||
want_errno = FALSE;
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_ERROR_REMOUNT_DEST:
|
||||
string = xasprintf ("Unable to remount destination \"%s\" with correct flags",
|
||||
failing_path);
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT:
|
||||
string = xasprintf ("Unable to apply mount flags: remount \"%s\"",
|
||||
failing_path);
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_SUCCESS:
|
||||
string = xstrdup ("Success");
|
||||
break;
|
||||
|
||||
default:
|
||||
string = xstrdup ("(unknown/invalid bind_mount_result)");
|
||||
break;
|
||||
}
|
||||
|
||||
if (want_errno_p != NULL)
|
||||
*want_errno_p = want_errno;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
void
|
||||
die_with_bind_result (bind_mount_result res,
|
||||
int saved_errno,
|
||||
const char *failing_path,
|
||||
const char *format,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
bool want_errno = TRUE;
|
||||
char *message;
|
||||
|
||||
fprintf (stderr, "bwrap: ");
|
||||
|
||||
va_start (args, format);
|
||||
vfprintf (stderr, format, args);
|
||||
va_end (args);
|
||||
|
||||
message = bind_mount_result_to_string (res, failing_path, &want_errno);
|
||||
fprintf (stderr, ": %s", message);
|
||||
/* message is leaked, but we're exiting unsuccessfully anyway, so ignore */
|
||||
|
||||
if (want_errno)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case BIND_MOUNT_ERROR_MOUNT:
|
||||
case BIND_MOUNT_ERROR_REMOUNT_DEST:
|
||||
case BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT:
|
||||
fprintf (stderr, ": %s", mount_strerror (saved_errno));
|
||||
break;
|
||||
|
||||
case BIND_MOUNT_ERROR_REALPATH_DEST:
|
||||
case BIND_MOUNT_ERROR_REOPEN_DEST:
|
||||
case BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD:
|
||||
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
|
||||
case BIND_MOUNT_SUCCESS:
|
||||
default:
|
||||
fprintf (stderr, ": %s", strerror (saved_errno));
|
||||
}
|
||||
}
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
exit (1);
|
||||
}
|
||||
|
|
32
bind-mount.h
32
bind-mount.h
|
@ -1,5 +1,6 @@
|
|||
/* bubblewrap
|
||||
* Copyright (C) 2016 Alexander Larsson
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -18,13 +19,36 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
typedef enum {
|
||||
BIND_READONLY = (1 << 0),
|
||||
BIND_DEVICES = (1 << 2),
|
||||
BIND_RECURSIVE = (1 << 3),
|
||||
} bind_option_t;
|
||||
|
||||
int bind_mount (int proc_fd,
|
||||
const char *src,
|
||||
const char *dest,
|
||||
bind_option_t options);
|
||||
typedef enum
|
||||
{
|
||||
BIND_MOUNT_SUCCESS = 0,
|
||||
BIND_MOUNT_ERROR_MOUNT,
|
||||
BIND_MOUNT_ERROR_REALPATH_DEST,
|
||||
BIND_MOUNT_ERROR_REOPEN_DEST,
|
||||
BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD,
|
||||
BIND_MOUNT_ERROR_FIND_DEST_MOUNT,
|
||||
BIND_MOUNT_ERROR_REMOUNT_DEST,
|
||||
BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT,
|
||||
} bind_mount_result;
|
||||
|
||||
bind_mount_result bind_mount (int proc_fd,
|
||||
const char *src,
|
||||
const char *dest,
|
||||
bind_option_t options,
|
||||
char **failing_path);
|
||||
|
||||
void die_with_bind_result (bind_mount_result res,
|
||||
int saved_errno,
|
||||
const char *failing_path,
|
||||
const char *format,
|
||||
...)
|
||||
__attribute__((__noreturn__))
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
|
|
802
bubblewrap.c
802
bubblewrap.c
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
|
@ -1,348 +0,0 @@
|
|||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2018-03-07.03; # UTC
|
||||
|
||||
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/lib$lib.a"; then
|
||||
found=yes
|
||||
lib=$dir/lib$lib.a
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
|
||||
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,791 +0,0 @@
|
|||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2018-03-07.03; # UTC
|
||||
|
||||
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||
as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by 'PROGRAMS ARGS'.
|
||||
object Object file output by 'PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputting dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "depcomp $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# Get the directory component of the given path, and save it in the
|
||||
# global variables '$dir'. Note that this directory component will
|
||||
# be either empty or ending with a '/' character. This is deliberate.
|
||||
set_dir_from ()
|
||||
{
|
||||
case $1 in
|
||||
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
|
||||
*) dir=;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Get the suffix-stripped basename of the given path, and save it the
|
||||
# global variable '$base'.
|
||||
set_base_from ()
|
||||
{
|
||||
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
|
||||
}
|
||||
|
||||
# If no dependency file was actually created by the compiler invocation,
|
||||
# we still have to create a dummy depfile, to avoid errors with the
|
||||
# Makefile "include basename.Plo" scheme.
|
||||
make_dummy_depfile ()
|
||||
{
|
||||
echo "#dummy" > "$depfile"
|
||||
}
|
||||
|
||||
# Factor out some common post-processing of the generated depfile.
|
||||
# Requires the auxiliary global variable '$tmpdepfile' to be set.
|
||||
aix_post_process_depfile ()
|
||||
{
|
||||
# If the compiler actually managed to produce a dependency file,
|
||||
# post-process it.
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form 'foo.o: dependency.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# $object: dependency.h
|
||||
# and one to simply output
|
||||
# dependency.h:
|
||||
# which is needed to avoid the deleted-header problem.
|
||||
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
|
||||
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
|
||||
} > "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
}
|
||||
|
||||
# A tabulation character.
|
||||
tab=' '
|
||||
# A newline character.
|
||||
nl='
|
||||
'
|
||||
# Character ranges might be problematic outside the C locale.
|
||||
# These definitions help.
|
||||
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
lower=abcdefghijklmnopqrstuvwxyz
|
||||
digits=0123456789
|
||||
alpha=${upper}${lower}
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||
depfile=${depfile-`echo "$object" |
|
||||
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Avoid interferences from the environment.
|
||||
gccflag= dashmflag=
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
# here, because this file can only contain one case statement.
|
||||
if test "$depmode" = hp; then
|
||||
# HP compiler uses -M and no extra arg.
|
||||
gccflag=-M
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
cygpath_u="cygpath -u -f -"
|
||||
if test "$depmode" = msvcmsys; then
|
||||
# This is just like msvisualcpp but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvisualcpp
|
||||
fi
|
||||
|
||||
if test "$depmode" = msvc7msys; then
|
||||
# This is just like msvc7 but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvc7
|
||||
fi
|
||||
|
||||
if test "$depmode" = xlc; then
|
||||
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
|
||||
gccflag=-qmakedep=gcc,-MF
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
gcc3)
|
||||
## gcc 3 implements dependency tracking that does exactly what
|
||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||
## the command line argument order; so add the flags where they
|
||||
## appear in depend2.am. Note that the slowdown incurred here
|
||||
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||
*) set fnord "$@" "$arg" ;;
|
||||
esac
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
|
||||
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
|
||||
## (see the conditional assignment to $gccflag above).
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say). Also, it might not be
|
||||
## supported by the other compilers which use the 'gcc' depmode.
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
gccflag=-MD,
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The second -e expression handles DOS-style file names with drive
|
||||
# letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the "deleted header file" problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
## Some versions of gcc put a space before the ':'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||
## to the object. Take care to not repeat it in the output.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
sgi)
|
||||
if test "$libtool" = yes; then
|
||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||
else
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
||||
| tr "$nl" ' ' >> "$depfile"
|
||||
echo >> "$depfile"
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> "$depfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
xlc)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
tcc)
|
||||
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
|
||||
# FIXME: That version still under development at the moment of writing.
|
||||
# Make that this statement remains true also for stable, released
|
||||
# versions.
|
||||
# It will wrap lines (doesn't matter whether long or short) with a
|
||||
# trailing '\', as in:
|
||||
#
|
||||
# foo.o : \
|
||||
# foo.c \
|
||||
# foo.h \
|
||||
#
|
||||
# It will put a trailing '\' even on the last line, and will use leading
|
||||
# spaces rather than leading tabs (at least since its commit 0394caf7
|
||||
# "Emit spaces for -MD").
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
|
||||
# We have to change lines of the first kind to '$object: \'.
|
||||
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
|
||||
# And for each line of the second kind, we have to emit a 'dep.h:'
|
||||
# dummy dependency, to avoid the deleted-header problem.
|
||||
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
## The order of this option in the case statement is important, since the
|
||||
## shell code in configure will try each of these formats in the order
|
||||
## listed in this file. A plain '-MD' option would be understood by many
|
||||
## compilers, so we must ensure this comes after the gcc and icc options.
|
||||
pgcc)
|
||||
# Portland's C compiler understands '-MD'.
|
||||
# Will always output deps to 'file.d' where file is the root name of the
|
||||
# source file under compilation, even if file resides in a subdirectory.
|
||||
# The object file name does not affect the name of the '.d' file.
|
||||
# pgcc 10.2 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using '\' :
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
set_dir_from "$object"
|
||||
# Use the source, not the object, to determine the base name, since
|
||||
# that's sadly what pgcc will do too.
|
||||
set_base_from "$source"
|
||||
tmpdepfile=$base.d
|
||||
|
||||
# For projects that build the same source file twice into different object
|
||||
# files, the pgcc approach of using the *source* file root name can cause
|
||||
# problems in parallel builds. Use a locking strategy to avoid stomping on
|
||||
# the same $tmpdepfile.
|
||||
lockdir=$base.d-lock
|
||||
trap "
|
||||
echo '$0: caught signal, cleaning up...' >&2
|
||||
rmdir '$lockdir'
|
||||
exit 1
|
||||
" 1 2 13 15
|
||||
numtries=100
|
||||
i=$numtries
|
||||
while test $i -gt 0; do
|
||||
# mkdir is a portable test-and-set.
|
||||
if mkdir "$lockdir" 2>/dev/null; then
|
||||
# This process acquired the lock.
|
||||
"$@" -MD
|
||||
stat=$?
|
||||
# Release the lock.
|
||||
rmdir "$lockdir"
|
||||
break
|
||||
else
|
||||
# If the lock is being held by a different process, wait
|
||||
# until the winning process is done or we timeout.
|
||||
while test -d "$lockdir" && test $i -gt 0; do
|
||||
sleep 1
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
fi
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
trap - 1 2 13 15
|
||||
if test $i -le 0; then
|
||||
echo "$0: failed to acquire lock after $numtries attempts" >&2
|
||||
echo "$0: check lockdir '$lockdir'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each line is of the form `foo.o: dependent.h',
|
||||
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp2)
|
||||
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||
# compilers, which have integrated preprocessors. The correct option
|
||||
# to use with these is +Maked; it writes dependencies to a file named
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
"$@" -Wc,+Maked
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add 'dependent.h:' lines.
|
||||
sed -ne '2,${
|
||||
s/^ *//
|
||||
s/ \\*$//
|
||||
s/$/:/
|
||||
p
|
||||
}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in 'foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# Libtool generates 2 separate objects for the 2 libraries. These
|
||||
# two compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
|
||||
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
# Same post-processing that is required for AIX mode.
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
msvc7)
|
||||
if test "$libtool" = yes; then
|
||||
showIncludes=-Wc,-showIncludes
|
||||
else
|
||||
showIncludes=-showIncludes
|
||||
fi
|
||||
"$@" $showIncludes > "$tmpdepfile"
|
||||
stat=$?
|
||||
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The first sed program below extracts the file names and escapes
|
||||
# backslashes for cygpath. The second sed program outputs the file
|
||||
# name when reading, but also accumulates all include files in the
|
||||
# hold buffer in order to output them again at the end. This only
|
||||
# works with sed implementations that can handle large buffers.
|
||||
sed < "$tmpdepfile" -n '
|
||||
/^Note: including file: *\(.*\)/ {
|
||||
s//\1/
|
||||
s/\\/\\\\/g
|
||||
p
|
||||
}' | $cygpath_u | sort -u | sed -n '
|
||||
s/ /\\ /g
|
||||
s/\(.*\)/'"$tab"'\1 \\/p
|
||||
s/.\(.*\) \\/\1:/
|
||||
H
|
||||
$ {
|
||||
s/.*/'"$tab"'/
|
||||
G
|
||||
p
|
||||
}' >> "$depfile"
|
||||
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7msys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# dependency tracking mechanisms from slower ones.
|
||||
|
||||
dashmstdout)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for ':'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this sed invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
dashXmstdout)
|
||||
# This case only exists to satisfy depend.m4. It is never actually
|
||||
# run, as this mode is specially recognized in the preamble.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no eat=no
|
||||
for arg
|
||||
do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
if test $eat = yes; then
|
||||
eat=no
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-arch)
|
||||
eat=yes ;;
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
# makedepend may prepend the VPATH from the source file name to the object.
|
||||
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process the last invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed '1,2d' "$tmpdepfile" \
|
||||
| tr ' ' "$nl" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
cpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E \
|
||||
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
| sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E 2>/dev/null |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||
echo "$tab" >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvcmsys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown depmode $depmode" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
|
@ -1,518 +0,0 @@
|
|||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2018-03-11.20; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# 'make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" $tab$nl"
|
||||
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) is_target_a_directory=never;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call 'install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename.
|
||||
if test -d "$dst"; then
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dstbase=`basename "$src"`
|
||||
case $dst in
|
||||
*/) dst=$dst$dstbase;;
|
||||
*) dst=$dst/$dstbase;;
|
||||
esac
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
case $dstdir in
|
||||
*/) dstdirslash=$dstdir;;
|
||||
*) dstdirslash=$dstdir/;;
|
||||
esac
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
# Note that $RANDOM variable is not portable (e.g. dash); Use it
|
||||
# here however when possible just to lower collision chance.
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
|
||||
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
# Because "mkdir -p" follows existing symlinks and we likely work
|
||||
# directly in world-writeable /tmp, make sure that the '$tmpdir'
|
||||
# directory is successfully created first before we actually test
|
||||
# 'mkdir -p' feature.
|
||||
if (umask $mkdir_umask &&
|
||||
$mkdirprog $mkdir_mode "$tmpdir" &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
test_tmpdir="$tmpdir/a"
|
||||
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=${dstdirslash}_inst.$$_
|
||||
rmtmp=${dstdirslash}_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
11147
build-aux/ltmain.sh
11147
build-aux/ltmain.sh
File diff suppressed because it is too large
Load Diff
|
@ -1,215 +0,0 @@
|
|||
#! /bin/sh
|
||||
# Common wrapper for a few potentially missing GNU programs.
|
||||
|
||||
scriptversion=2018-03-07.03; # UTC
|
||||
|
||||
# Copyright (C) 1996-2018 Free Software Foundation, Inc.
|
||||
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
|
||||
--is-lightweight)
|
||||
# Used by our autoconf macros to check whether the available missing
|
||||
# script is modern enough.
|
||||
exit 0
|
||||
;;
|
||||
|
||||
--run)
|
||||
# Back-compat with the calling convention used by older automake.
|
||||
shift
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
|
||||
to PROGRAM being missing or too old.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal autoconf autoheader autom4te automake makeinfo
|
||||
bison yacc flex lex help2man
|
||||
|
||||
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
|
||||
'g' are ignored when checking the name.
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "missing $scriptversion (GNU Automake)"
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: unknown '$1' option"
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# Run the given program, remember its exit status.
|
||||
"$@"; st=$?
|
||||
|
||||
# If it succeeded, we are done.
|
||||
test $st -eq 0 && exit 0
|
||||
|
||||
# Also exit now if we it failed (or wasn't found), and '--version' was
|
||||
# passed; such an option is passed most likely to detect whether the
|
||||
# program is present and works.
|
||||
case $2 in --version|--help) exit $st;; esac
|
||||
|
||||
# Exit code 63 means version mismatch. This often happens when the user
|
||||
# tries to use an ancient version of a tool on a file that requires a
|
||||
# minimum version.
|
||||
if test $st -eq 63; then
|
||||
msg="probably too old"
|
||||
elif test $st -eq 127; then
|
||||
# Program was missing.
|
||||
msg="missing on your system"
|
||||
else
|
||||
# Program was found and executed, but failed. Give up.
|
||||
exit $st
|
||||
fi
|
||||
|
||||
perl_URL=https://www.perl.org/
|
||||
flex_URL=https://github.com/westes/flex
|
||||
gnu_software_URL=https://www.gnu.org/software
|
||||
|
||||
program_details ()
|
||||
{
|
||||
case $1 in
|
||||
aclocal|automake)
|
||||
echo "The '$1' program is part of the GNU Automake package:"
|
||||
echo "<$gnu_software_URL/automake>"
|
||||
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/autoconf>"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
autoconf|autom4te|autoheader)
|
||||
echo "The '$1' program is part of the GNU Autoconf package:"
|
||||
echo "<$gnu_software_URL/autoconf/>"
|
||||
echo "It also requires GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
give_advice ()
|
||||
{
|
||||
# Normalize program name to check for.
|
||||
normalized_program=`echo "$1" | sed '
|
||||
s/^gnu-//; t
|
||||
s/^gnu//; t
|
||||
s/^g//; t'`
|
||||
|
||||
printf '%s\n' "'$1' is $msg."
|
||||
|
||||
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
|
||||
case $normalized_program in
|
||||
autoconf*)
|
||||
echo "You should only need it if you modified 'configure.ac',"
|
||||
echo "or m4 files included by it."
|
||||
program_details 'autoconf'
|
||||
;;
|
||||
autoheader*)
|
||||
echo "You should only need it if you modified 'acconfig.h' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'autoheader'
|
||||
;;
|
||||
automake*)
|
||||
echo "You should only need it if you modified 'Makefile.am' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'automake'
|
||||
;;
|
||||
aclocal*)
|
||||
echo "You should only need it if you modified 'acinclude.m4' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'aclocal'
|
||||
;;
|
||||
autom4te*)
|
||||
echo "You might have modified some maintainer files that require"
|
||||
echo "the 'autom4te' program to be rebuilt."
|
||||
program_details 'autom4te'
|
||||
;;
|
||||
bison*|yacc*)
|
||||
echo "You should only need it if you modified a '.y' file."
|
||||
echo "You may want to install the GNU Bison package:"
|
||||
echo "<$gnu_software_URL/bison/>"
|
||||
;;
|
||||
lex*|flex*)
|
||||
echo "You should only need it if you modified a '.l' file."
|
||||
echo "You may want to install the Fast Lexical Analyzer package:"
|
||||
echo "<$flex_URL>"
|
||||
;;
|
||||
help2man*)
|
||||
echo "You should only need it if you modified a dependency" \
|
||||
"of a man page."
|
||||
echo "You may want to install the GNU Help2man package:"
|
||||
echo "<$gnu_software_URL/help2man/>"
|
||||
;;
|
||||
makeinfo*)
|
||||
echo "You should only need it if you modified a '.texi' file, or"
|
||||
echo "any other file indirectly affecting the aspect of the manual."
|
||||
echo "You might want to install the Texinfo package:"
|
||||
echo "<$gnu_software_URL/texinfo/>"
|
||||
echo "The spurious makeinfo call might also be the consequence of"
|
||||
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
|
||||
echo "want to install GNU make:"
|
||||
echo "<$gnu_software_URL/make/>"
|
||||
;;
|
||||
*)
|
||||
echo "You might have modified some files without having the proper"
|
||||
echo "tools for further handling them. Check the 'README' file, it"
|
||||
echo "often tells you about the needed prerequisites for installing"
|
||||
echo "this package. You may also peek at any GNU archive site, in"
|
||||
echo "case some other package contains this missing '$1' program."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
give_advice "$1" | sed -e '1s/^/WARNING: /' \
|
||||
-e '2,$s/^/ /' >&2
|
||||
|
||||
# Propagate the correct exit status (expected to be 127 for a program
|
||||
# not found, 63 for a program that failed due to version mismatch).
|
||||
exit $st
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
|
@ -1,651 +0,0 @@
|
|||
#! /bin/sh
|
||||
# Copyright (C) 2011-2018 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
scriptversion=2013-12-23.17; # UTC
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
me=tap-driver.sh
|
||||
|
||||
fatal ()
|
||||
{
|
||||
echo "$me: fatal: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$me: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--ignore-exit]
|
||||
[--diagnostic-string=STRING] [--merge|--no-merge]
|
||||
[--comments|--no-comments] [--] TEST-COMMAND
|
||||
The '--test-name', '-log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
# TODO: better error handling in option parsing (in particular, ensure
|
||||
# TODO: $log_file, $trs_file and $test_name are defined).
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the result and output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=0
|
||||
color_tests=0
|
||||
merge=0
|
||||
ignore_exit=0
|
||||
comments=0
|
||||
diag_string='#'
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "$me $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) shift;; # No-op.
|
||||
--merge) merge=1;;
|
||||
--no-merge) merge=0;;
|
||||
--ignore-exit) ignore_exit=1;;
|
||||
--comments) comments=1;;
|
||||
--no-comments) comments=0;;
|
||||
--diagnostic-string) diag_string=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
test $# -gt 0 || usage_error "missing test command"
|
||||
|
||||
case $expect_failure in
|
||||
yes) expect_failure=1;;
|
||||
*) expect_failure=0;;
|
||||
esac
|
||||
|
||||
if test $color_tests = yes; then
|
||||
init_colors='
|
||||
color_map["red"]="[0;31m" # Red.
|
||||
color_map["grn"]="[0;32m" # Green.
|
||||
color_map["lgn"]="[1;32m" # Light green.
|
||||
color_map["blu"]="[1;34m" # Blue.
|
||||
color_map["mgn"]="[0;35m" # Magenta.
|
||||
color_map["std"]="[m" # No color.
|
||||
color_for_result["ERROR"] = "mgn"
|
||||
color_for_result["PASS"] = "grn"
|
||||
color_for_result["XPASS"] = "red"
|
||||
color_for_result["FAIL"] = "red"
|
||||
color_for_result["XFAIL"] = "lgn"
|
||||
color_for_result["SKIP"] = "blu"'
|
||||
else
|
||||
init_colors=''
|
||||
fi
|
||||
|
||||
# :; is there to work around a bug in bash 3.2 (and earlier) which
|
||||
# does not always set '$?' properly on redirection failure.
|
||||
# See the Autoconf manual for more details.
|
||||
:;{
|
||||
(
|
||||
# Ignore common signals (in this subshell only!), to avoid potential
|
||||
# problems with Korn shells. Some Korn shells are known to propagate
|
||||
# to themselves signals that have killed a child process they were
|
||||
# waiting for; this is done at least for SIGINT (and usually only for
|
||||
# it, in truth). Without the `trap' below, such a behaviour could
|
||||
# cause a premature exit in the current subshell, e.g., in case the
|
||||
# test command it runs gets terminated by a SIGINT. Thus, the awk
|
||||
# script we are piping into would never seen the exit status it
|
||||
# expects on its last input line (which is displayed below by the
|
||||
# last `echo $?' statement), and would thus die reporting an internal
|
||||
# error.
|
||||
# For more information, see the Autoconf manual and the threads:
|
||||
# <https://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html>
|
||||
# <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html>
|
||||
trap : 1 3 2 13 15
|
||||
if test $merge -gt 0; then
|
||||
exec 2>&1
|
||||
else
|
||||
exec 2>&3
|
||||
fi
|
||||
"$@"
|
||||
echo $?
|
||||
) | LC_ALL=C ${AM_TAP_AWK-awk} \
|
||||
-v me="$me" \
|
||||
-v test_script_name="$test_name" \
|
||||
-v log_file="$log_file" \
|
||||
-v trs_file="$trs_file" \
|
||||
-v expect_failure="$expect_failure" \
|
||||
-v merge="$merge" \
|
||||
-v ignore_exit="$ignore_exit" \
|
||||
-v comments="$comments" \
|
||||
-v diag_string="$diag_string" \
|
||||
'
|
||||
# TODO: the usages of "cat >&3" below could be optimized when using
|
||||
# GNU awk, and/on on systems that supports /dev/fd/.
|
||||
|
||||
# Implementation note: in what follows, `result_obj` will be an
|
||||
# associative array that (partly) simulates a TAP result object
|
||||
# from the `TAP::Parser` perl module.
|
||||
|
||||
## ----------- ##
|
||||
## FUNCTIONS ##
|
||||
## ----------- ##
|
||||
|
||||
function fatal(msg)
|
||||
{
|
||||
print me ": " msg | "cat >&2"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function abort(where)
|
||||
{
|
||||
fatal("internal error " where)
|
||||
}
|
||||
|
||||
# Convert a boolean to a "yes"/"no" string.
|
||||
function yn(bool)
|
||||
{
|
||||
return bool ? "yes" : "no";
|
||||
}
|
||||
|
||||
function add_test_result(result)
|
||||
{
|
||||
if (!test_results_index)
|
||||
test_results_index = 0
|
||||
test_results_list[test_results_index] = result
|
||||
test_results_index += 1
|
||||
test_results_seen[result] = 1;
|
||||
}
|
||||
|
||||
# Whether the test script should be re-run by "make recheck".
|
||||
function must_recheck()
|
||||
{
|
||||
for (k in test_results_seen)
|
||||
if (k != "XFAIL" && k != "PASS" && k != "SKIP")
|
||||
return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
# Whether the content of the log file associated to this test should
|
||||
# be copied into the "global" test-suite.log.
|
||||
function copy_in_global_log()
|
||||
{
|
||||
for (k in test_results_seen)
|
||||
if (k != "PASS")
|
||||
return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
function get_global_test_result()
|
||||
{
|
||||
if ("ERROR" in test_results_seen)
|
||||
return "ERROR"
|
||||
if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
|
||||
return "FAIL"
|
||||
all_skipped = 1
|
||||
for (k in test_results_seen)
|
||||
if (k != "SKIP")
|
||||
all_skipped = 0
|
||||
if (all_skipped)
|
||||
return "SKIP"
|
||||
return "PASS";
|
||||
}
|
||||
|
||||
function stringify_result_obj(result_obj)
|
||||
{
|
||||
if (result_obj["is_unplanned"] || result_obj["number"] != testno)
|
||||
return "ERROR"
|
||||
|
||||
if (plan_seen == LATE_PLAN)
|
||||
return "ERROR"
|
||||
|
||||
if (result_obj["directive"] == "TODO")
|
||||
return result_obj["is_ok"] ? "XPASS" : "XFAIL"
|
||||
|
||||
if (result_obj["directive"] == "SKIP")
|
||||
return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL;
|
||||
|
||||
if (length(result_obj["directive"]))
|
||||
abort("in function stringify_result_obj()")
|
||||
|
||||
return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
|
||||
}
|
||||
|
||||
function decorate_result(result)
|
||||
{
|
||||
color_name = color_for_result[result]
|
||||
if (color_name)
|
||||
return color_map[color_name] "" result "" color_map["std"]
|
||||
# If we are not using colorized output, or if we do not know how
|
||||
# to colorize the given result, we should return it unchanged.
|
||||
return result
|
||||
}
|
||||
|
||||
function report(result, details)
|
||||
{
|
||||
if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/)
|
||||
{
|
||||
msg = ": " test_script_name
|
||||
add_test_result(result)
|
||||
}
|
||||
else if (result == "#")
|
||||
{
|
||||
msg = " " test_script_name ":"
|
||||
}
|
||||
else
|
||||
{
|
||||
abort("in function report()")
|
||||
}
|
||||
if (length(details))
|
||||
msg = msg " " details
|
||||
# Output on console might be colorized.
|
||||
print decorate_result(result) msg
|
||||
# Log the result in the log file too, to help debugging (this is
|
||||
# especially true when said result is a TAP error or "Bail out!").
|
||||
print result msg | "cat >&3";
|
||||
}
|
||||
|
||||
function testsuite_error(error_message)
|
||||
{
|
||||
report("ERROR", "- " error_message)
|
||||
}
|
||||
|
||||
function handle_tap_result()
|
||||
{
|
||||
details = result_obj["number"];
|
||||
if (length(result_obj["description"]))
|
||||
details = details " " result_obj["description"]
|
||||
|
||||
if (plan_seen == LATE_PLAN)
|
||||
{
|
||||
details = details " # AFTER LATE PLAN";
|
||||
}
|
||||
else if (result_obj["is_unplanned"])
|
||||
{
|
||||
details = details " # UNPLANNED";
|
||||
}
|
||||
else if (result_obj["number"] != testno)
|
||||
{
|
||||
details = sprintf("%s # OUT-OF-ORDER (expecting %d)",
|
||||
details, testno);
|
||||
}
|
||||
else if (result_obj["directive"])
|
||||
{
|
||||
details = details " # " result_obj["directive"];
|
||||
if (length(result_obj["explanation"]))
|
||||
details = details " " result_obj["explanation"]
|
||||
}
|
||||
|
||||
report(stringify_result_obj(result_obj), details)
|
||||
}
|
||||
|
||||
# `skip_reason` should be empty whenever planned > 0.
|
||||
function handle_tap_plan(planned, skip_reason)
|
||||
{
|
||||
planned += 0 # Avoid getting confused if, say, `planned` is "00"
|
||||
if (length(skip_reason) && planned > 0)
|
||||
abort("in function handle_tap_plan()")
|
||||
if (plan_seen)
|
||||
{
|
||||
# Error, only one plan per stream is acceptable.
|
||||
testsuite_error("multiple test plans")
|
||||
return;
|
||||
}
|
||||
planned_tests = planned
|
||||
# The TAP plan can come before or after *all* the TAP results; we speak
|
||||
# respectively of an "early" or a "late" plan. If we see the plan line
|
||||
# after at least one TAP result has been seen, assume we have a late
|
||||
# plan; in this case, any further test result seen after the plan will
|
||||
# be flagged as an error.
|
||||
plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN)
|
||||
# If testno > 0, we have an error ("too many tests run") that will be
|
||||
# automatically dealt with later, so do not worry about it here. If
|
||||
# $plan_seen is true, we have an error due to a repeated plan, and that
|
||||
# has already been dealt with above. Otherwise, we have a valid "plan
|
||||
# with SKIP" specification, and should report it as a particular kind
|
||||
# of SKIP result.
|
||||
if (planned == 0 && testno == 0)
|
||||
{
|
||||
if (length(skip_reason))
|
||||
skip_reason = "- " skip_reason;
|
||||
report("SKIP", skip_reason);
|
||||
}
|
||||
}
|
||||
|
||||
function extract_tap_comment(line)
|
||||
{
|
||||
if (index(line, diag_string) == 1)
|
||||
{
|
||||
# Strip leading `diag_string` from `line`.
|
||||
line = substr(line, length(diag_string) + 1)
|
||||
# And strip any leading and trailing whitespace left.
|
||||
sub("^[ \t]*", "", line)
|
||||
sub("[ \t]*$", "", line)
|
||||
# Return what is left (if any).
|
||||
return line;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
# When this function is called, we know that line is a TAP result line,
|
||||
# so that it matches the (perl) RE "^(not )?ok\b".
|
||||
function setup_result_obj(line)
|
||||
{
|
||||
# Get the result, and remove it from the line.
|
||||
result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0)
|
||||
sub("^(not )?ok[ \t]*", "", line)
|
||||
|
||||
# If the result has an explicit number, get it and strip it; otherwise,
|
||||
# automatically assing the next progresive number to it.
|
||||
if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/)
|
||||
{
|
||||
match(line, "^[0-9]+")
|
||||
# The final `+ 0` is to normalize numbers with leading zeros.
|
||||
result_obj["number"] = substr(line, 1, RLENGTH) + 0
|
||||
line = substr(line, RLENGTH + 1)
|
||||
}
|
||||
else
|
||||
{
|
||||
result_obj["number"] = testno
|
||||
}
|
||||
|
||||
if (plan_seen == LATE_PLAN)
|
||||
# No further test results are acceptable after a "late" TAP plan
|
||||
# has been seen.
|
||||
result_obj["is_unplanned"] = 1
|
||||
else if (plan_seen && testno > planned_tests)
|
||||
result_obj["is_unplanned"] = 1
|
||||
else
|
||||
result_obj["is_unplanned"] = 0
|
||||
|
||||
# Strip trailing and leading whitespace.
|
||||
sub("^[ \t]*", "", line)
|
||||
sub("[ \t]*$", "", line)
|
||||
|
||||
# This will have to be corrected if we have a "TODO"/"SKIP" directive.
|
||||
result_obj["description"] = line
|
||||
result_obj["directive"] = ""
|
||||
result_obj["explanation"] = ""
|
||||
|
||||
if (index(line, "#") == 0)
|
||||
return # No possible directive, nothing more to do.
|
||||
|
||||
# Directives are case-insensitive.
|
||||
rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*"
|
||||
|
||||
# See whether we have the directive, and if yes, where.
|
||||
pos = match(line, rx "$")
|
||||
if (!pos)
|
||||
pos = match(line, rx "[^a-zA-Z0-9_]")
|
||||
|
||||
# If there was no TAP directive, we have nothing more to do.
|
||||
if (!pos)
|
||||
return
|
||||
|
||||
# Let`s now see if the TAP directive has been escaped. For example:
|
||||
# escaped: ok \# SKIP
|
||||
# not escaped: ok \\# SKIP
|
||||
# escaped: ok \\\\\# SKIP
|
||||
# not escaped: ok \ # SKIP
|
||||
if (substr(line, pos, 1) == "#")
|
||||
{
|
||||
bslash_count = 0
|
||||
for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--)
|
||||
bslash_count += 1
|
||||
if (bslash_count % 2)
|
||||
return # Directive was escaped.
|
||||
}
|
||||
|
||||
# Strip the directive and its explanation (if any) from the test
|
||||
# description.
|
||||
result_obj["description"] = substr(line, 1, pos - 1)
|
||||
# Now remove the test description from the line, that has been dealt
|
||||
# with already.
|
||||
line = substr(line, pos)
|
||||
# Strip the directive, and save its value (normalized to upper case).
|
||||
sub("^[ \t]*#[ \t]*", "", line)
|
||||
result_obj["directive"] = toupper(substr(line, 1, 4))
|
||||
line = substr(line, 5)
|
||||
# Now get the explanation for the directive (if any), with leading
|
||||
# and trailing whitespace removed.
|
||||
sub("^[ \t]*", "", line)
|
||||
sub("[ \t]*$", "", line)
|
||||
result_obj["explanation"] = line
|
||||
}
|
||||
|
||||
function get_test_exit_message(status)
|
||||
{
|
||||
if (status == 0)
|
||||
return ""
|
||||
if (status !~ /^[1-9][0-9]*$/)
|
||||
abort("getting exit status")
|
||||
if (status < 127)
|
||||
exit_details = ""
|
||||
else if (status == 127)
|
||||
exit_details = " (command not found?)"
|
||||
else if (status >= 128 && status <= 255)
|
||||
exit_details = sprintf(" (terminated by signal %d?)", status - 128)
|
||||
else if (status > 256 && status <= 384)
|
||||
# We used to report an "abnormal termination" here, but some Korn
|
||||
# shells, when a child process die due to signal number n, can leave
|
||||
# in $? an exit status of 256+n instead of the more standard 128+n.
|
||||
# Apparently, both behaviours are allowed by POSIX (2008), so be
|
||||
# prepared to handle them both. See also Austing Group report ID
|
||||
# 0000051 <http://www.austingroupbugs.net/view.php?id=51>
|
||||
exit_details = sprintf(" (terminated by signal %d?)", status - 256)
|
||||
else
|
||||
# Never seen in practice.
|
||||
exit_details = " (abnormal termination)"
|
||||
return sprintf("exited with status %d%s", status, exit_details)
|
||||
}
|
||||
|
||||
function write_test_results()
|
||||
{
|
||||
print ":global-test-result: " get_global_test_result() > trs_file
|
||||
print ":recheck: " yn(must_recheck()) > trs_file
|
||||
print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file
|
||||
for (i = 0; i < test_results_index; i += 1)
|
||||
print ":test-result: " test_results_list[i] > trs_file
|
||||
close(trs_file);
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
|
||||
## ------- ##
|
||||
## SETUP ##
|
||||
## ------- ##
|
||||
|
||||
'"$init_colors"'
|
||||
|
||||
# Properly initialized once the TAP plan is seen.
|
||||
planned_tests = 0
|
||||
|
||||
COOKED_PASS = expect_failure ? "XPASS": "PASS";
|
||||
COOKED_FAIL = expect_failure ? "XFAIL": "FAIL";
|
||||
|
||||
# Enumeration-like constants to remember which kind of plan (if any)
|
||||
# has been seen. It is important that NO_PLAN evaluates "false" as
|
||||
# a boolean.
|
||||
NO_PLAN = 0
|
||||
EARLY_PLAN = 1
|
||||
LATE_PLAN = 2
|
||||
|
||||
testno = 0 # Number of test results seen so far.
|
||||
bailed_out = 0 # Whether a "Bail out!" directive has been seen.
|
||||
|
||||
# Whether the TAP plan has been seen or not, and if yes, which kind
|
||||
# it is ("early" is seen before any test result, "late" otherwise).
|
||||
plan_seen = NO_PLAN
|
||||
|
||||
## --------- ##
|
||||
## PARSING ##
|
||||
## --------- ##
|
||||
|
||||
is_first_read = 1
|
||||
|
||||
while (1)
|
||||
{
|
||||
# Involutions required so that we are able to read the exit status
|
||||
# from the last input line.
|
||||
st = getline
|
||||
if (st < 0) # I/O error.
|
||||
fatal("I/O error while reading from input stream")
|
||||
else if (st == 0) # End-of-input
|
||||
{
|
||||
if (is_first_read)
|
||||
abort("in input loop: only one input line")
|
||||
break
|
||||
}
|
||||
if (is_first_read)
|
||||
{
|
||||
is_first_read = 0
|
||||
nextline = $0
|
||||
continue
|
||||
}
|
||||
else
|
||||
{
|
||||
curline = nextline
|
||||
nextline = $0
|
||||
$0 = curline
|
||||
}
|
||||
# Copy any input line verbatim into the log file.
|
||||
print | "cat >&3"
|
||||
# Parsing of TAP input should stop after a "Bail out!" directive.
|
||||
if (bailed_out)
|
||||
continue
|
||||
|
||||
# TAP test result.
|
||||
if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/)
|
||||
{
|
||||
testno += 1
|
||||
setup_result_obj($0)
|
||||
handle_tap_result()
|
||||
}
|
||||
# TAP plan (normal or "SKIP" without explanation).
|
||||
else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/)
|
||||
{
|
||||
# The next two lines will put the number of planned tests in $0.
|
||||
sub("^1\\.\\.", "")
|
||||
sub("[^0-9]*$", "")
|
||||
handle_tap_plan($0, "")
|
||||
continue
|
||||
}
|
||||
# TAP "SKIP" plan, with an explanation.
|
||||
else if ($0 ~ /^1\.\.0+[ \t]*#/)
|
||||
{
|
||||
# The next lines will put the skip explanation in $0, stripping
|
||||
# any leading and trailing whitespace. This is a little more
|
||||
# tricky in truth, since we want to also strip a potential leading
|
||||
# "SKIP" string from the message.
|
||||
sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "")
|
||||
sub("[ \t]*$", "");
|
||||
handle_tap_plan(0, $0)
|
||||
}
|
||||
# "Bail out!" magic.
|
||||
# Older versions of prove and TAP::Harness (e.g., 3.17) did not
|
||||
# recognize a "Bail out!" directive when preceded by leading
|
||||
# whitespace, but more modern versions (e.g., 3.23) do. So we
|
||||
# emulate the latter, "more modern" behaviour.
|
||||
else if ($0 ~ /^[ \t]*Bail out!/)
|
||||
{
|
||||
bailed_out = 1
|
||||
# Get the bailout message (if any), with leading and trailing
|
||||
# whitespace stripped. The message remains stored in `$0`.
|
||||
sub("^[ \t]*Bail out![ \t]*", "");
|
||||
sub("[ \t]*$", "");
|
||||
# Format the error message for the
|
||||
bailout_message = "Bail out!"
|
||||
if (length($0))
|
||||
bailout_message = bailout_message " " $0
|
||||
testsuite_error(bailout_message)
|
||||
}
|
||||
# Maybe we have too look for dianogtic comments too.
|
||||
else if (comments != 0)
|
||||
{
|
||||
comment = extract_tap_comment($0);
|
||||
if (length(comment))
|
||||
report("#", comment);
|
||||
}
|
||||
}
|
||||
|
||||
## -------- ##
|
||||
## FINISH ##
|
||||
## -------- ##
|
||||
|
||||
# A "Bail out!" directive should cause us to ignore any following TAP
|
||||
# error, as well as a non-zero exit status from the TAP producer.
|
||||
if (!bailed_out)
|
||||
{
|
||||
if (!plan_seen)
|
||||
{
|
||||
testsuite_error("missing test plan")
|
||||
}
|
||||
else if (planned_tests != testno)
|
||||
{
|
||||
bad_amount = testno > planned_tests ? "many" : "few"
|
||||
testsuite_error(sprintf("too %s tests run (expected %d, got %d)",
|
||||
bad_amount, planned_tests, testno))
|
||||
}
|
||||
if (!ignore_exit)
|
||||
{
|
||||
# Fetch exit status from the last line.
|
||||
exit_message = get_test_exit_message(nextline)
|
||||
if (exit_message)
|
||||
testsuite_error(exit_message)
|
||||
}
|
||||
}
|
||||
|
||||
write_test_results()
|
||||
|
||||
exit 0
|
||||
|
||||
} # End of "BEGIN" block.
|
||||
'
|
||||
|
||||
# TODO: document that we consume the file descriptor 3 :-(
|
||||
} 3>"$log_file"
|
||||
|
||||
test $? -eq 0 || fatal "I/O or internal error"
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
|
@ -1,148 +0,0 @@
|
|||
#! /bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
scriptversion=2018-03-07.03; # UTC
|
||||
|
||||
# Copyright (C) 2011-2018 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$0: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--]
|
||||
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=no
|
||||
color_tests=no
|
||||
enable_hard_errors=yes
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
missing_opts=
|
||||
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
|
||||
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
|
||||
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
|
||||
if test x"$missing_opts" != x; then
|
||||
usage_error "the following mandatory options are missing:$missing_opts"
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
usage_error "missing argument"
|
||||
fi
|
||||
|
||||
if test $color_tests = yes; then
|
||||
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||
red='[0;31m' # Red.
|
||||
grn='[0;32m' # Green.
|
||||
lgn='[1;32m' # Light green.
|
||||
blu='[1;34m' # Blue.
|
||||
mgn='[0;35m' # Magenta.
|
||||
std='[m' # No color.
|
||||
else
|
||||
red= grn= lgn= blu= mgn= std=
|
||||
fi
|
||||
|
||||
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||
trap "st=129; $do_exit" 1
|
||||
trap "st=130; $do_exit" 2
|
||||
trap "st=141; $do_exit" 13
|
||||
trap "st=143; $do_exit" 15
|
||||
|
||||
# Test script is run here.
|
||||
"$@" >$log_file 2>&1
|
||||
estatus=$?
|
||||
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
tweaked_estatus=1
|
||||
else
|
||||
tweaked_estatus=$estatus
|
||||
fi
|
||||
|
||||
case $tweaked_estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report the test outcome and exit status in the logs, so that one can
|
||||
# know whether the test passed or failed simply by looking at the '.log'
|
||||
# file, without the need of also peaking into the corresponding '.trs'
|
||||
# file (automake bug#11814).
|
||||
echo "$res $test_name (exit status: $estatus)" >>$log_file
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
# Register the test result, and other relevant metadata.
|
||||
echo ":test-result: $res" > $trs_file
|
||||
echo ":global-test-result: $res" >> $trs_file
|
||||
echo ":recheck: $recheck" >> $trs_file
|
||||
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
196
bwrap.xml
196
bwrap.xml
|
@ -6,7 +6,7 @@
|
|||
|
||||
<refentryinfo>
|
||||
<title>bwrap</title>
|
||||
<productname>Project Atomic</productname>
|
||||
<productname>Containers</productname>
|
||||
<authorgroup>
|
||||
<author>
|
||||
<contrib>Developer</contrib>
|
||||
|
@ -42,7 +42,8 @@
|
|||
|
||||
<refsect1><title>Description</title>
|
||||
<para>
|
||||
<command>bwrap</command> is a privileged helper for container setup. You
|
||||
<command>bwrap</command> is a unprivileged low-level sandboxing tool
|
||||
(optionally setuid on older distributions). You
|
||||
are unlikely to use it directly from the commandline, although that is possible.
|
||||
</para>
|
||||
<para>
|
||||
|
@ -91,6 +92,10 @@
|
|||
multiple sources.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--argv0 <arg choice="plain">VALUE</arg></option></term>
|
||||
<listitem><para>Set argv[0] to the value <arg choice="plain">VALUE</arg> before running the program</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>Options related to kernel namespaces:</para>
|
||||
<variablelist>
|
||||
|
@ -130,16 +135,45 @@
|
|||
<term><option>--unshare-all</option></term>
|
||||
<listitem><para>Unshare all possible namespaces. Currently equivalent with: <option>--unshare-user-try</option> <option>--unshare-ipc</option> <option>--unshare-pid</option> <option>--unshare-net</option> <option>--unshare-uts</option> <option>--unshare-cgroup-try</option></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--share-net</option></term>
|
||||
<listitem><para>Retain the network namespace, overriding an earlier <option>--unshare-all</option> or <option>--unshare-net</option></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--userns <arg choice="plain">FD</arg></option></term>
|
||||
<listitem><para>Use an existing user namespace instead of creating a new one. The namespace must fulfil the permission requirements for setns(), which generally means that it must be a decendant of the currently active user namespace, owned by the same user. </para>
|
||||
<listitem><para>Use an existing user namespace instead of creating a new one. The namespace must fulfil the permission requirements for setns(), which generally means that it must be a descendant of the currently active user namespace, owned by the same user. </para>
|
||||
<para>This is incompatible with --unshare-user, and doesn't work in the setuid version of bubblewrap.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--userns2 <arg choice="plain">FD</arg></option></term>
|
||||
<listitem><para>After setting up the new namespace, switch into the specified namespace. For this to work the specified namespace must be a decendant of the user namespace used for the setup, so this is only useful in combination with --userns.</para>
|
||||
<listitem><para>After setting up the new namespace, switch into the specified namespace. For this to work the specified namespace must be a descendant of the user namespace used for the setup, so this is only useful in combination with --userns.</para>
|
||||
<para>This is useful because sometimes bubblewrap itself creates nested user namespaces (to work around some kernel issues) and --userns2 can be used to enter these.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--disable-userns</option></term>
|
||||
<listitem><para>
|
||||
Prevent the process in the sandbox from creating further user namespaces,
|
||||
so that it cannot rearrange the filesystem namespace or do other more
|
||||
complex namespace modification.
|
||||
This is currently implemented by setting the
|
||||
<literal>user.max_user_namespaces</literal> sysctl to 1, and then
|
||||
entering a nested user namespace which is unable to raise that limit
|
||||
in the outer namespace.
|
||||
This option requires <option>--unshare-user</option>, and doesn't work
|
||||
in the setuid version of bubblewrap.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--assert-userns-disabled</option></term>
|
||||
<listitem><para>
|
||||
Confirm that the process in the sandbox has been prevented from
|
||||
creating further user namespaces, but without taking any particular
|
||||
action to prevent that. For example, this can be combined with
|
||||
<option>--userns</option> to check that the given user namespace
|
||||
has already been set up to prevent the creation of further user
|
||||
namespaces.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--pidns <arg choice="plain">FD</arg></option></term>
|
||||
<listitem><para>Use an existing pid namespace instead of creating one. This is often used with --userns, because the pid namespace must be owned by the same user namespace that bwrap uses. </para>
|
||||
|
@ -172,6 +206,12 @@
|
|||
<term><option>--unsetenv <arg choice="plain">VAR</arg></option></term>
|
||||
<listitem><para>Unset an environment variable</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--clearenv</option></term>
|
||||
<listitem><para>Unset all environment variables, except for
|
||||
<envar>PWD</envar> and any that are subsequently set by
|
||||
<option>--setenv</option></para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>Options for monitoring the sandbox from the outside:</para>
|
||||
<variablelist>
|
||||
|
@ -190,10 +230,45 @@
|
|||
<para>
|
||||
Filesystem related options. These are all operations that modify the filesystem directly, or
|
||||
mounts stuff in the filesystem. These are applied in the order they are given as arguments.
|
||||
</para>
|
||||
<para>
|
||||
Any missing parent directories that are required to create a specified destination are
|
||||
automatically created as needed.
|
||||
automatically created as needed. Their permissions are normally set to 0755
|
||||
(rwxr-xr-x). However, if a <option>--perms</option> option is in effect, and
|
||||
it sets the permissions for group or other to zero, then newly-created
|
||||
parent directories will also have their corresponding permission set to zero.
|
||||
<option>--size</option> modifies the size of the created mount when preceding a
|
||||
<option>--tmpfs</option> action; <option>--perms</option> and <option>--size</option>
|
||||
can be combined.
|
||||
</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--perms <arg choice="plain">OCTAL</arg></option></term>
|
||||
<listitem><para>This option does nothing on its own, and must be followed
|
||||
by one of the options that it affects. It sets the permissions
|
||||
for the next operation to <arg choice="plain">OCTAL</arg>.
|
||||
Subsequent operations are not affected: for example,
|
||||
<literal>--perms 0700 --tmpfs /a --tmpfs /b</literal> will mount
|
||||
<filename>/a</filename> with permissions 0700, then return to
|
||||
the default permissions for <filename>/b</filename>.
|
||||
Note that <option>--perms</option> and <option>--size</option> can be
|
||||
combined: <literal>--perms 0700 --size 10485760 --tmpfs /s</literal> will apply
|
||||
permissions as well as a maximum size to the created tmpfs.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--size <arg choice="plain">BYTES</arg></option></term>
|
||||
<listitem><para>This option does nothing on its own, and must be followed
|
||||
by <literal>--tmpfs</literal>. It sets the size in bytes for the next tmpfs.
|
||||
For example, <literal>--size 10485760 --tmpfs /tmp</literal> will create a tmpfs
|
||||
at <filename>/tmp</filename> of size 10MiB. Subsequent operations are not
|
||||
affected: for example,
|
||||
<literal>--size 10485760 --tmpfs /a --tmpfs /b</literal> will mount
|
||||
<filename>/a</filename> with size 10MiB, then return to the default size for
|
||||
<filename>/b</filename>.
|
||||
Note that <option>--perms</option> and <option>--size</option> can be
|
||||
combined: <literal>--size 10485760 --perms 0700 --tmpfs /s</literal> will apply
|
||||
permissions as well as a maximum size to the created tmpfs.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Bind mount the host path <arg choice="plain">SRC</arg> on <arg choice="plain">DEST</arg></para></listitem>
|
||||
|
@ -232,7 +307,13 @@
|
|||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--tmpfs <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Mount new tmpfs on <arg choice="plain">DEST</arg></para></listitem>
|
||||
<listitem>
|
||||
<para>Mount new tmpfs on <arg choice="plain">DEST</arg>.
|
||||
If the previous option was <option>--perms</option>, it sets the
|
||||
mode of the tmpfs. Otherwise, the tmpfs has mode 0755.
|
||||
If the previous option was <option>--size</option>, it sets the
|
||||
size in bytes of the tmpfs. Otherwise, the tmpfs has the default size.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--mqueue <arg choice="plain">DEST</arg></option></term>
|
||||
|
@ -240,23 +321,68 @@
|
|||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--dir <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Create a directory at <arg choice="plain">DEST</arg></para></listitem>
|
||||
<listitem>
|
||||
<para>Create a directory at <arg choice="plain">DEST</arg>.
|
||||
If the directory already exists, its permissions are unmodified,
|
||||
ignoring <option>--perms</option> (use <option>--chmod</option>
|
||||
if the permissions of an existing directory need to be changed).
|
||||
If the directory is newly created and the previous option was
|
||||
<option>--perms</option>, it sets the mode of the directory.
|
||||
Otherwise, newly-created directories have mode 0755.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--file <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Copy from the file descriptor <arg choice="plain">FD</arg> to <arg choice="plain">DEST</arg></para></listitem>
|
||||
<listitem>
|
||||
<para>Copy from the file descriptor <arg choice="plain">FD</arg> to
|
||||
<arg choice="plain">DEST</arg>.
|
||||
If the previous option was <option>--perms</option>, it sets the
|
||||
mode of the new file. Otherwise, the file has mode 0666
|
||||
(note that this is not the same as <option>--bind-data</option>).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--bind-data <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Copy from the file descriptor <arg choice="plain">FD</arg> to a file which is bind-mounted on <arg choice="plain">DEST</arg></para></listitem>
|
||||
<listitem>
|
||||
<para>Copy from the file descriptor <arg choice="plain">FD</arg> to
|
||||
a file which is bind-mounted on <arg choice="plain">DEST</arg>.
|
||||
If the previous option was <option>--perms</option>, it sets the
|
||||
mode of the new file. Otherwise, the file has mode 0600
|
||||
(note that this is not the same as <option>--file</option>).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--ro-bind-data <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Copy from the file descriptor <arg choice="plain">FD</arg> to a file which is bind-mounted readonly on <arg choice="plain">DEST</arg></para></listitem>
|
||||
<listitem>
|
||||
<para>Copy from the file descriptor <arg choice="plain">FD</arg> to
|
||||
a file which is bind-mounted read-only on
|
||||
<arg choice="plain">DEST</arg>.
|
||||
If the previous option was <option>--perms</option>, it sets the
|
||||
mode of the new file. Otherwise, the file has mode 0600
|
||||
(note that this is not the same as <option>--file</option>).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--symlink <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Create a symlink at <arg choice="plain">DEST</arg> with target <arg choice="plain">SRC</arg></para></listitem>
|
||||
<listitem>
|
||||
<para>Create a symlink at <arg choice="plain">DEST</arg> with target
|
||||
<arg choice="plain">SRC</arg>.</para>
|
||||
<para>Since version 0.9.0, it is not considered to be an error if
|
||||
<arg choice="plain">DEST</arg> already exists as a symbolic link and its
|
||||
target is exactly <arg choice="plain">SRC</arg>.</para>
|
||||
<para>Before version 0.9.0, if <arg choice="plain">DEST</arg> already
|
||||
existed, this would be treated as an error (even if its target
|
||||
was identical to <arg choice="plain">SRC</arg>).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--chmod <arg choice="plain">OCTAL</arg> <arg choice="plain">PATH</arg></option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Set the permissions of <arg choice="plain">PATH</arg>, which
|
||||
must already exist, to <arg choice="plain">OCTAL</arg>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>Lockdown options:</para>
|
||||
|
@ -265,8 +391,25 @@
|
|||
<term><option>--seccomp <arg choice="plain">FD</arg></option></term>
|
||||
<listitem><para>
|
||||
Load and use seccomp rules from <arg choice="plain">FD</arg>.
|
||||
The rules need to be in the form of a compiled eBPF program,
|
||||
The rules need to be in the form of a compiled cBPF program,
|
||||
as generated by seccomp_export_bpf.
|
||||
If this option is given more than once, only the last one is used.
|
||||
Use <option>--add-seccomp-fd</option> if multiple seccomp programs
|
||||
are needed.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--add-seccomp-fd <arg choice="plain">FD</arg></option></term>
|
||||
<listitem><para>
|
||||
Load and use seccomp rules from <arg choice="plain">FD</arg>.
|
||||
The rules need to be in the form of a compiled cBPF program,
|
||||
as generated by seccomp_export_bpf.
|
||||
This option can be repeated, in which case all the seccomp
|
||||
programs will be loaded in the order given (note that the kernel
|
||||
will evaluate them in reverse order, so the last program on the
|
||||
bwrap command-line is evaluated first). All of them, except
|
||||
possibly the last, must allow use of the PR_SET_SECCOMP prctl.
|
||||
This option cannot be combined with <option>--seccomp</option>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
@ -303,6 +446,28 @@
|
|||
Write information in JSON format about the sandbox to FD.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--json-status-fd <arg choice="plain">FD</arg></option></term>
|
||||
<listitem><para>
|
||||
Multiple JSON documents are written to <arg choice="plain">FD</arg>,
|
||||
one per line (<ulink url="https://jsonlines.org/">"JSON lines" format</ulink>).
|
||||
Each line is a single JSON object.
|
||||
After <command>bwrap</command> has started the child process inside the sandbox,
|
||||
it writes an object with a <literal>child-pid</literal> member to the
|
||||
<option>--json-status-fd</option> (this duplicates the older <option>--info-fd</option>).
|
||||
The corresponding value is the process ID of the child process in the pid namespace from
|
||||
which <command>bwrap</command> was run.
|
||||
If available, the namespace IDs are also included in the object with the <literal>child-pid</literal>;
|
||||
again, this duplicates the older <option>--info-fd</option>.
|
||||
When the child process inside the sandbox exits, <command>bwrap</command> writes an object
|
||||
with an exit-code member, and then closes the <option>--json-status-fd</option>. The value
|
||||
corresponding to <literal>exit-code</literal> is the exit status of the child, in the usual
|
||||
shell encoding (n if it exited normally with status n, or 128+n if it was killed by signal n).
|
||||
Other members may be added to those objects in future versions of <command>bwrap</command>,
|
||||
and other JSON objects may be added before or after the current objects, so readers must
|
||||
ignore members and objects that they do not understand.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--new-session</option></term>
|
||||
<listitem><para>
|
||||
|
@ -312,7 +477,9 @@
|
|||
</para><para>
|
||||
Note: In a general sandbox, if you don't use --new-session, it is
|
||||
recommended to use seccomp to disallow the TIOCSTI ioctl, otherwise
|
||||
the application can feed keyboard input to the terminal.
|
||||
the application can feed keyboard input to the terminal
|
||||
which can e.g. lead to out-of-sandbox command execution
|
||||
(see CVE-2017-5226).
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
@ -333,7 +500,8 @@
|
|||
<varlistentry>
|
||||
<term><option>--cap-add <arg choice="plain">CAP</arg></option></term>
|
||||
<listitem><para>
|
||||
Add the specified capability when running as privileged user. It accepts
|
||||
Add the specified capability <arg choice="plain">CAP</arg>, e.g.
|
||||
CAP_DAC_READ_SEARCH, when running as privileged user. It accepts
|
||||
the special value ALL to add all the permitted caps.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2021 Simon McVittie
|
||||
# SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
usage() {
|
||||
if [ "${1-2}" -ne 0 ]; then
|
||||
exec >&2
|
||||
fi
|
||||
cat <<EOF
|
||||
Usage: see source code
|
||||
EOF
|
||||
exit "${1-2}"
|
||||
}
|
||||
|
||||
opt_clang=
|
||||
|
||||
getopt_temp="help"
|
||||
getopt_temp="$getopt_temp,clang"
|
||||
|
||||
getopt_temp="$(getopt -o '' --long "${getopt_temp}" -n "$0" -- "$@")"
|
||||
eval set -- "$getopt_temp"
|
||||
unset getopt_temp
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
(--clang)
|
||||
clang=yes
|
||||
shift
|
||||
;;
|
||||
|
||||
(--help)
|
||||
usage 0
|
||||
# not reached
|
||||
;;
|
||||
|
||||
(--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
|
||||
(*)
|
||||
echo 'Error parsing options' >&2
|
||||
usage 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# No more arguments please
|
||||
for arg in "$@"; do
|
||||
usage 2
|
||||
done
|
||||
|
||||
if dpkg-vendor --derives-from Debian; then
|
||||
apt-get -y update
|
||||
apt-get -q -y install \
|
||||
autoconf \
|
||||
automake \
|
||||
build-essential \
|
||||
docbook-xml \
|
||||
docbook-xsl \
|
||||
libcap-dev \
|
||||
libselinux1-dev \
|
||||
libtool \
|
||||
meson \
|
||||
pkg-config \
|
||||
python3 \
|
||||
xsltproc \
|
||||
${NULL+}
|
||||
|
||||
if [ -n "${opt_clang}" ]; then
|
||||
apt-get -y install clang
|
||||
fi
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if command -v yum; then
|
||||
yum -y install \
|
||||
'pkgconfig(libselinux)' \
|
||||
/usr/bin/eu-readelf \
|
||||
autoconf \
|
||||
automake \
|
||||
docbook-style-xsl \
|
||||
gcc \
|
||||
git \
|
||||
libasan \
|
||||
libcap-devel \
|
||||
libtool \
|
||||
libtsan \
|
||||
libubsan \
|
||||
libxslt \
|
||||
make \
|
||||
meson \
|
||||
redhat-rpm-config \
|
||||
rsync \
|
||||
${NULL+}
|
||||
|
||||
if [ -n "${opt_clang}" ]; then
|
||||
yum -y install clang
|
||||
fi
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Unknown distribution" >&2
|
||||
exit 1
|
||||
|
||||
# vim:set sw=4 sts=4 et:
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# shellcheck shell=bash
|
||||
|
||||
# bash completion file for bubblewrap commands
|
||||
#
|
||||
|
||||
|
@ -7,31 +7,39 @@ _bwrap() {
|
|||
local cur prev words cword
|
||||
_init_completion || return
|
||||
|
||||
# Please keep sorted in LC_ALL=C order
|
||||
local boolean_options="
|
||||
--as-pid-1
|
||||
--assert-userns-disabled
|
||||
--clearenv
|
||||
--disable-userns
|
||||
--help
|
||||
--new-session
|
||||
--unshare-all
|
||||
--unshare-cgroup
|
||||
--unshare-cgroup-try
|
||||
--unshare-user
|
||||
--unshare-user-try
|
||||
--unshare-all
|
||||
--unshare-ipc
|
||||
--unshare-net
|
||||
--unshare-pid
|
||||
--unshare-user
|
||||
--unshare-user-try
|
||||
--unshare-uts
|
||||
--version
|
||||
"
|
||||
|
||||
# Please keep sorted in LC_ALL=C order
|
||||
local options_with_args="
|
||||
$boolean_optons
|
||||
--add-seccomp-fd
|
||||
--args
|
||||
--argv0
|
||||
--bind
|
||||
--bind-data
|
||||
--block-fd
|
||||
--cap-add
|
||||
--cap-drop
|
||||
--chdir
|
||||
--chmod
|
||||
--dev
|
||||
--dev-bind
|
||||
--die-with-parent
|
||||
|
@ -43,11 +51,13 @@ _bwrap() {
|
|||
--hostname
|
||||
--info-fd
|
||||
--lock-file
|
||||
--perms
|
||||
--proc
|
||||
--remount-ro
|
||||
--ro-bind
|
||||
--seccomp
|
||||
--setenv
|
||||
--size
|
||||
--symlink
|
||||
--sync-fd
|
||||
--uid
|
||||
|
@ -55,10 +65,12 @@ _bwrap() {
|
|||
--userns-block-fd
|
||||
"
|
||||
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||
fi
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
|
||||
fi
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
complete -F _bwrap bwrap
|
||||
|
||||
# vim:set ft=bash:
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
bash_completion_dir = get_option('bash_completion_dir')
|
||||
|
||||
if bash_completion_dir == ''
|
||||
bash_completion = dependency(
|
||||
'bash-completion',
|
||||
version : '>=2.0',
|
||||
required : false,
|
||||
)
|
||||
|
||||
if bash_completion.found()
|
||||
if meson.version().version_compare('>=0.51.0')
|
||||
bash_completion_dir = bash_completion.get_variable(
|
||||
default_value: '',
|
||||
pkgconfig: 'completionsdir',
|
||||
pkgconfig_define: [
|
||||
'prefix', get_option('prefix'),
|
||||
'datadir', get_option('prefix') / get_option('datadir'),
|
||||
],
|
||||
)
|
||||
else
|
||||
bash_completion_dir = bash_completion.get_pkgconfig_variable(
|
||||
'completionsdir',
|
||||
default: '',
|
||||
define_variable: [
|
||||
'datadir', get_option('prefix') / get_option('datadir'),
|
||||
],
|
||||
)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if bash_completion_dir == ''
|
||||
bash_completion_dir = get_option('datadir') / 'bash-completion' / 'completions'
|
||||
endif
|
||||
|
||||
install_data('bwrap', install_dir : bash_completion_dir)
|
|
@ -0,0 +1,7 @@
|
|||
if get_option('bash_completion').enabled()
|
||||
subdir('bash')
|
||||
endif
|
||||
|
||||
if get_option('zsh_completion').enabled()
|
||||
subdir('zsh')
|
||||
endif
|
|
@ -0,0 +1,115 @@
|
|||
#compdef bwrap
|
||||
|
||||
_bwrap_args_after_perms_size=(
|
||||
# Please sort alphabetically (in LC_ALL=C order) by option name
|
||||
'--tmpfs[Mount new tmpfs on DEST]:mount point for tmpfs:_files -/'
|
||||
)
|
||||
|
||||
_bwrap_args_after_perms=(
|
||||
# Please sort alphabetically (in LC_ALL=C order) by option name
|
||||
'--bind-data[Copy from FD to file which is bind-mounted on DEST]: :_guard "[0-9]#" "file descriptor to read content":destination:_files'
|
||||
'--dir[Create dir at DEST]:directory to create:_files -/'
|
||||
'--file[Copy from FD to destination DEST]: :_guard "[0-9]#" "file descriptor to read content from":destination:_files'
|
||||
'--ro-bind-data[Copy from FD to file which is readonly bind-mounted on DEST]: :_guard "[0-9]#" "file descriptor to read content from":destination:_files'
|
||||
'--size[Set size in bytes for next action argument]: :->after_perms_size'
|
||||
'--tmpfs[Mount new tmpfs on DEST]:mount point for tmpfs:_files -/'
|
||||
)
|
||||
|
||||
_bwrap_args_after_size=(
|
||||
# Please sort alphabetically (in LC_ALL=C order) by option name
|
||||
'--perms[Set permissions for next action argument]: :_guard "[0-7]#" "permissions in octal": :->after_perms_size'
|
||||
'--tmpfs[Mount new tmpfs on DEST]:mount point for tmpfs:_files -/'
|
||||
)
|
||||
|
||||
_bwrap_args=(
|
||||
'*::arguments:_normal'
|
||||
$_bwrap_args_after_perms
|
||||
|
||||
# Please sort alphabetically (in LC_ALL=C order) by option name
|
||||
'--add-seccomp-fd[Load and use seccomp rules from FD]: :_guard "[0-9]#" "file descriptor to read seccomp rules from"'
|
||||
'--assert-userns-disabled[Fail unless further use of user namespace inside sandbox is disabled]'
|
||||
'--args[Parse NUL-separated args from FD]: :_guard "[0-9]#" "file descriptor with NUL-separated arguments"'
|
||||
'--argv0[Set argv0 to the value VALUE before running the program]:value:'
|
||||
'--as-pid-1[Do not install a reaper process with PID=1]'
|
||||
'--bind-try[Equal to --bind but ignores non-existent SRC]:source:_files:destination:_files'
|
||||
'--bind[Bind mount the host path SRC on DEST]:source:_files:destination:_files'
|
||||
'--block-fd[Block on FD until some data to read is available]: :_guard "[0-9]#" "file descriptor to block on"'
|
||||
'--cap-add[Add cap CAP when running as privileged user]:capability to add:->caps'
|
||||
'--cap-drop[Drop cap CAP when running as privileged user]:capability to add:->caps'
|
||||
'--chdir[Change directory to DIR]:working directory for sandbox: _files -/'
|
||||
'--chmod[Set permissions]: :_guard "[0-7]#" "permissions in octal":path to set permissions:_files'
|
||||
'--clearenv[Unset all environment variables]'
|
||||
'--dev-bind-try[Equal to --dev-bind but ignores non-existent SRC]:source:_files:destination:_files'
|
||||
'--dev-bind[Bind mount the host path SRC on DEST, allowing device access]:source:_files:destination:_files'
|
||||
'--dev[Mount new dev on DEST]:mount point for /dev:_files -/'
|
||||
"--die-with-parent[Kills with SIGKILL child process (COMMAND) when bwrap or bwrap's parent dies.]"
|
||||
'--disable-userns[Disable further use of user namespaces inside sandbox]'
|
||||
'--exec-label[Exec label for the sandbox]:SELinux label:_selinux_contexts'
|
||||
'--file-label[File label for temporary sandbox content]:SELinux label:_selinux_contexts'
|
||||
'--gid[Custom gid in the sandbox (requires --unshare-user or --userns)]: :_guard "[0-9]#" "numeric group ID"'
|
||||
'--help[Print help and exit]'
|
||||
'--hostname[Custom hostname in the sandbox (requires --unshare-uts)]:hostname:'
|
||||
'--info-fd[Write information about the running container to FD]: :_guard "[0-9]#" "file descriptor to write to"'
|
||||
'--json-status-fd[Write container status to FD as multiple JSON documents]: :_guard "[0-9]#" "file descriptor to write to"'
|
||||
'--lock-file[Take a lock on DEST while sandbox is running]:lock file:_files'
|
||||
'--mqueue[Mount new mqueue on DEST]:mount point for mqueue:_files -/'
|
||||
'--new-session[Create a new terminal session]'
|
||||
'--perms[Set permissions for next action argument]: :_guard "[0-7]#" "permissions in octal": :->after_perms'
|
||||
'--pidns[Use this user namespace (as parent namespace if using --unshare-pid)]: :'
|
||||
'--proc[Mount new procfs on DEST]:mount point for procfs:_files -/'
|
||||
'--remount-ro[Remount DEST as readonly; does not recursively remount]:mount point to remount read-only:_files'
|
||||
'--ro-bind-try[Equal to --ro-bind but ignores non-existent SRC]:source:_files:destination:_files'
|
||||
'--ro-bind[Bind mount the host path SRC readonly on DEST]:source:_files:destination:_files'
|
||||
'--seccomp[Load and use seccomp rules from FD]: :_guard "[0-9]#" "file descriptor to read seccomp rules from"'
|
||||
'--setenv[Set an environment variable]:variable to set:_parameters -g "*export*":value of variable: :'
|
||||
'--size[Set size in bytes for next action argument]: :->after_size'
|
||||
'--symlink[Create symlink at DEST with target SRC]:symlink target:_files:symlink to create:_files:'
|
||||
'--sync-fd[Keep this fd open while sandbox is running]: :_guard "[0-9]#" "file descriptor to keep open"'
|
||||
'--uid[Custom uid in the sandbox (requires --unshare-user or --userns)]: :_guard "[0-9]#" "numeric group ID"'
|
||||
'(--clearenv)--unsetenv[Unset an environment variable]:variable to unset:_parameters -g "*export*"'
|
||||
'--unshare-all[Unshare every namespace we support by default]'
|
||||
'--unshare-cgroup-try[Create new cgroup namespace if possible else continue by skipping it]'
|
||||
'--unshare-cgroup[Create new cgroup namespace]'
|
||||
'--unshare-ipc[Create new ipc namespace]'
|
||||
'--unshare-net[Create new network namespace]'
|
||||
'--unshare-pid[Create new pid namespace]'
|
||||
'(--userns --userns2)--unshare-user[Create new user namespace (may be automatically implied if not setuid)]'
|
||||
'--unshare-user-try[Create new user namespace if possible else continue by skipping it]'
|
||||
'--unshare-uts[Create new uts namespace]'
|
||||
'(--unshare-user)--userns[Use this user namespace (cannot combine with --unshare-user)]: :'
|
||||
'--userns-block-fd[Block on FD until the user namespace is ready]: :_guard "[0-9]#" "file descriptor to block on"'
|
||||
'(--unshare-user)--userns2[After setup switch to this user namespace, only useful with --userns]: :'
|
||||
'--version[Print version]'
|
||||
)
|
||||
|
||||
_bwrap() {
|
||||
_arguments -S $_bwrap_args
|
||||
case "$state" in
|
||||
after_perms)
|
||||
_values -S ' ' 'option' $_bwrap_args_after_perms
|
||||
;;
|
||||
|
||||
after_size)
|
||||
_values -S ' ' 'option' $_bwrap_args_after_size
|
||||
;;
|
||||
|
||||
after_perms_size)
|
||||
_values -S ' ' 'option' $_bwrap_args_after_perms_size
|
||||
;;
|
||||
|
||||
caps)
|
||||
# $ grep -E '#define\sCAP_\w+\s+[0-9]+' /usr/include/linux/capability.h | awk '{print $2}' | xargs echo
|
||||
local all_caps=(
|
||||
CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_FSETID \
|
||||
CAP_KILL CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_LINUX_IMMUTABLE \
|
||||
CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_ADMIN CAP_NET_RAW \
|
||||
CAP_IPC_LOCK CAP_IPC_OWNER CAP_SYS_MODULE CAP_SYS_RAWIO CAP_SYS_CHROOT \
|
||||
CAP_SYS_PTRACE CAP_SYS_PACCT CAP_SYS_ADMIN CAP_SYS_BOOT CAP_SYS_NICE \
|
||||
CAP_SYS_RESOURCE CAP_SYS_TIME CAP_SYS_TTY_CONFIG CAP_MKNOD CAP_LEASE \
|
||||
CAP_AUDIT_WRITE CAP_AUDIT_CONTROL CAP_SETFCAP CAP_MAC_OVERRIDE \
|
||||
CAP_MAC_ADMIN CAP_SYSLOG CAP_WAKE_ALARM CAP_BLOCK_SUSPEND CAP_AUDIT_READ
|
||||
)
|
||||
_values 'caps' $all_caps
|
||||
;;
|
||||
esac
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
zsh_completion_dir = get_option('zsh_completion_dir')
|
||||
|
||||
if zsh_completion_dir == ''
|
||||
zsh_completion_dir = get_option('datadir') / 'zsh' / 'site-functions'
|
||||
endif
|
||||
|
||||
install_data('_bwrap', install_dir : zsh_completion_dir)
|
104
config.h.in
104
config.h.in
|
@ -1,104 +0,0 @@
|
|||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if userns should be used by default in suid mode */
|
||||
#undef ENABLE_REQUIRE_USERNS
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the `cap' library (-lcap). */
|
||||
#undef HAVE_LIBCAP
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define if SELinux is available */
|
||||
#undef HAVE_SELINUX
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/capability.h> header file. */
|
||||
#undef HAVE_SYS_CAPABILITY_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Enable extensions on AIX 3, Interix. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# undef _ALL_SOURCE
|
||||
#endif
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# undef _GNU_SOURCE
|
||||
#endif
|
||||
/* Enable threading extensions on Solaris. */
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# undef _POSIX_PTHREAD_SEMANTICS
|
||||
#endif
|
||||
/* Enable extensions on HP NonStop. */
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# undef _TANDEM_SOURCE
|
||||
#endif
|
||||
/* Enable general extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# undef __EXTENSIONS__
|
||||
#endif
|
||||
|
||||
|
||||
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||
# define _DARWIN_USE_64_BIT_INODE 1
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
/* Define to 1 if on MINIX. */
|
||||
#undef _MINIX
|
||||
|
||||
/* Define to 2 if the system does not provide POSIX.1 features except with
|
||||
this defined. */
|
||||
#undef _POSIX_1_SOURCE
|
||||
|
||||
/* Define to 1 if you need to in order for `stat' and other things to work. */
|
||||
#undef _POSIX_SOURCE
|
46
configure.ac
46
configure.ac
|
@ -1,5 +1,5 @@
|
|||
AC_PREREQ([2.63])
|
||||
AC_INIT([bubblewrap], [0.4.0], [atomic-devel@projectatomic.io])
|
||||
AC_INIT([bubblewrap], [0.9.0], [atomic-devel@projectatomic.io])
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
|
@ -14,6 +14,7 @@ AC_SYS_LARGEFILE
|
|||
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
PKG_PROG_PKG_CONFIG([])
|
||||
|
||||
AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])])
|
||||
|
||||
|
@ -41,35 +42,55 @@ AC_ARG_WITH([bash-completion-dir],
|
|||
[],
|
||||
[with_bash_completion_dir=yes])
|
||||
|
||||
if test "x$with_bash_completion_dir" = "xyes"; then
|
||||
AS_IF([test "x$with_bash_completion_dir" = "xyes"],
|
||||
[
|
||||
PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
|
||||
[BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"],
|
||||
[BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
|
||||
else
|
||||
],
|
||||
[
|
||||
BASH_COMPLETION_DIR="$with_bash_completion_dir"
|
||||
fi
|
||||
])
|
||||
|
||||
AC_SUBST([BASH_COMPLETION_DIR])
|
||||
AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"])
|
||||
|
||||
AC_ARG_WITH([zsh-completion-dir],
|
||||
AS_HELP_STRING([--with-zsh-completion-dir[=PATH]],
|
||||
[Install the zsh auto-completion script in this directory. @<:@default=yes@:>@]),
|
||||
[],
|
||||
[with_zsh_completion_dir=yes])
|
||||
|
||||
AS_IF([test "x$with_zsh_completion_dir" = "xyes"],
|
||||
[ZSH_COMPLETION_DIR="$datadir/zsh/site-functions"],
|
||||
[ZSH_COMPLETION_DIR="$with_zsh_completion_dir"])
|
||||
|
||||
|
||||
AC_SUBST([ZSH_COMPLETION_DIR])
|
||||
AM_CONDITIONAL([ENABLE_ZSH_COMPLETION], [test "x$with_zsh_completion_dir" != "xno"])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
have_selinux=no
|
||||
AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support]))
|
||||
if test "x$enable_selinux" != "xno"; then
|
||||
AS_IF([test "x$enable_selinux" != "xno"], [
|
||||
PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9],
|
||||
[AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available])
|
||||
have_selinux=yes
|
||||
M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"],
|
||||
[have_selinux=no])
|
||||
if test "x$have_selinux" = xno -a "x$enable_selinux" = xyes; then
|
||||
AC_MSG_ERROR([*** SELinux support requested but libraries not found])
|
||||
fi
|
||||
fi
|
||||
AS_IF([test "x$have_selinux" = xno && test "x$enable_selinux" = xyes],
|
||||
[AC_MSG_ERROR([*** SELinux support requested but libraries not found])])
|
||||
PKG_CHECK_MODULES([SELINUX_2_3], [libselinux >= 2.3],
|
||||
[AC_DEFINE(HAVE_SELINUX_2_3, 1, [Define if SELinux is version >= 2.3])],
|
||||
[:])
|
||||
])
|
||||
AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"])
|
||||
|
||||
dnl Keep this in sync with ostree, except remove -Werror=declaration-after-statement
|
||||
CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\
|
||||
-pipe \
|
||||
-Wall \
|
||||
-Werror=shadow \
|
||||
-Werror=empty-body \
|
||||
-Werror=strict-prototypes \
|
||||
-Werror=missing-prototypes \
|
||||
|
@ -84,14 +105,15 @@ CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\
|
|||
-Werror=incompatible-pointer-types \
|
||||
-Werror=misleading-indentation \
|
||||
-Werror=missing-include-dirs -Werror=aggregate-return \
|
||||
-Werror=switch-default \
|
||||
-Wswitch-enum \
|
||||
])
|
||||
AC_SUBST(WARN_CFLAGS)
|
||||
|
||||
AC_CHECK_LIB(cap, cap_from_text)
|
||||
|
||||
if test "$ac_cv_lib_cap_cap_from_text" != "yes"; then
|
||||
AC_MSG_ERROR([*** libcap requested but not found])
|
||||
fi
|
||||
AS_IF([test "$ac_cv_lib_cap_cap_from_text" != "yes"],
|
||||
[AC_MSG_ERROR([*** libcap requested but not found])])
|
||||
|
||||
AC_ARG_WITH(priv-mode,
|
||||
AS_HELP_STRING([--with-priv-mode=setuid/none],
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
demos/*
|
|
@ -1,24 +0,0 @@
|
|||
bubblewrap (0.4.0-ok4) yangtze; urgency=medium
|
||||
|
||||
* update version info
|
||||
|
||||
-- luzhiping <luzhiping@kylinos.cn> Mon, 22 Aug 2022 13:52:33 +0800
|
||||
|
||||
bubblewrap (0.4.0-ok3) yangtze; urgency=medium
|
||||
|
||||
* fix debian/rules
|
||||
|
||||
-- Luoyaoming <luoyaoming@kylinos.cn> Sat, 04 Jun 2022 12:59:22 +0800
|
||||
|
||||
bubblewrap (0.4.0-ok2) yangtze; urgency=medium
|
||||
|
||||
* debian/rules:
|
||||
fix openkylin vendor
|
||||
|
||||
-- Luoyaoming <luoyaoming@kylinos.cn> Fri, 03 Jun 2022 14:18:53 +0800
|
||||
|
||||
bubblewrap (0.4.0-ok1) yangtze; urgency=medium
|
||||
|
||||
* Build for openKylin.
|
||||
|
||||
-- openKylinBot <openKylinBot@openkylin.com> Mon, 25 Apr 2022 22:03:04 +0800
|
|
@ -1 +0,0 @@
|
|||
config.log
|
|
@ -1,36 +0,0 @@
|
|||
Source: bubblewrap
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: Openkylin Developers <packaging@lists.openkylin.top>
|
||||
XSBC-Original-Maintainer: Utopia Maintenance Team <pkg-utopia-maintainers@lists.alioth.debian.org>
|
||||
Uploaders:
|
||||
Laszlo Boszormenyi (GCS) <gcs@debian.org>,
|
||||
Simon McVittie <smcv@debian.org>,
|
||||
Build-Depends:
|
||||
automake (>= 1.14.1),
|
||||
bash-completion,
|
||||
debhelper-compat (= 12),
|
||||
docbook-xml,
|
||||
docbook-xsl,
|
||||
libcap-dev,
|
||||
libselinux1-dev (>= 2.1.9),
|
||||
pkg-config,
|
||||
python3 <!nocheck>,
|
||||
xsltproc,
|
||||
Standards-Version: 4.4.1
|
||||
Homepage: https://github.com/projectatomic/bubblewrap
|
||||
Vcs-Git: https://salsa.debian.org/debian/bubblewrap.git
|
||||
Vcs-Browser: https://salsa.debian.org/debian/bubblewrap
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: bubblewrap
|
||||
Architecture: linux-any
|
||||
Multi-arch: foreign
|
||||
Depends:
|
||||
${misc:Depends},
|
||||
${shlibs:Depends},
|
||||
Breaks:
|
||||
flatpak (<< 0.8.0-0),
|
||||
Description: setuid wrapper for unprivileged chroot and namespace manipulation
|
||||
Core execution engine for unprivileged containers that works as a setuid
|
||||
binary on kernels without user namespaces.
|
|
@ -1,68 +0,0 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: bubblewrap
|
||||
Source: https://github.com/projectatomic/bubblewrap/
|
||||
|
||||
Files: *
|
||||
Copyright: 2016 Alexander Larsson
|
||||
License: LGPL-2+
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2016 Laszlo Boszormenyi (GCS) <gcs@debian.org>
|
||||
License: LGPL-2+
|
||||
|
||||
Files: m4/attributes.m4
|
||||
Copyright:
|
||||
2006-2008 Diego Pettenò <flameeyes@gmail.com>
|
||||
2006-2008 xine project
|
||||
2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
|
||||
License: GPL-2+ with Autoconf exception
|
||||
|
||||
License: LGPL-2+
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
USA.
|
||||
.
|
||||
On Debian systems, the full text of the GNU Library General Public License
|
||||
version 2 can be found in the file `/usr/share/common-licenses/LGPL-2'.
|
||||
|
||||
License: GPL-2+ with Autoconf exception
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301, USA.
|
||||
.
|
||||
As a special exception, the copyright owners of the
|
||||
macro gives unlimited permission to copy, distribute and modify the
|
||||
configure scripts that are the output of Autoconf when processing the
|
||||
Macro. You need not follow the terms of the GNU General Public
|
||||
License when using or distributing such scripts, even though portions
|
||||
of the text of the Macro appear in them. The GNU General Public
|
||||
License (GPL) does govern all other use of the material that
|
||||
constitutes the Autoconf Macro.
|
||||
.
|
||||
This special exception to the GPL applies to versions of the
|
||||
Autoconf Macro released by this project. When you make and
|
||||
distribute a modified version of the Autoconf Macro, you may extend
|
||||
this special exception to the GPL to apply to your modified version as
|
||||
well.
|
|
@ -1 +0,0 @@
|
|||
README.md
|
|
@ -1,2 +0,0 @@
|
|||
# this is known and intentional
|
||||
bubblewrap: setuid-binary usr/bin/bwrap 4755 root/root
|
|
@ -1,42 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+pie,+bindnow
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
PKGDIR=$(CURDIR)/debian/bubblewrap
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_fixperms:
|
||||
chmod a+x $(PKGDIR)/usr/share/bash-completion/completions/bwrap
|
||||
# Ubuntu enables unprivileged user namespaces; no need for bwrap to be suid
|
||||
# there.
|
||||
ifneq (yes,$(shell dpkg-vendor --derives-from Ubuntu && echo yes))
|
||||
chmod 04755 $(PKGDIR)/usr/bin/bwrap
|
||||
dh_fixperms -Xbin/bwrap
|
||||
else
|
||||
dh_fixperms
|
||||
endif
|
||||
|
||||
ifneq (yes,$(shell dpkg-vendor --derives-from openKylin && echo yes))
|
||||
chmod 04755 $(PKGDIR)/usr/bin/bwrap
|
||||
dh_fixperms -Xbin/bwrap
|
||||
else
|
||||
dh_fixperms
|
||||
endif
|
||||
|
||||
.PHONY: override_dh_fixperms
|
||||
|
||||
override_dh_auto_test:
|
||||
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
|
||||
# Remove LD_PRELOAD so we don't run with faketime. It uses
|
||||
# sem_open(), but bubblewrap runs in an environment where that
|
||||
# can't work.
|
||||
env -u LD_PRELOAD dh_auto_test
|
||||
endif
|
||||
|
||||
.PHONY: override_dh_auto_test
|
|
@ -1,3 +0,0 @@
|
|||
include:
|
||||
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
|
||||
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
|
|
@ -1 +0,0 @@
|
|||
3.0 (native)
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# vim:set sw=4 sts=4 et ft=perl:
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use IPC::Run qw(run);
|
||||
|
||||
sub run_ok {
|
||||
my $argv = shift;
|
||||
my $debug = join(' ', @$argv);
|
||||
ok(run($argv, @_), qq{"$debug" should succeed});
|
||||
}
|
||||
|
||||
my $out;
|
||||
run_ok([qw(bwrap --ro-bind / / /usr/bin/id -u)], '<', \undef, '>', \$out);
|
||||
is($out, `id -u`);
|
||||
run_ok([qw(bwrap --ro-bind / / /usr/bin/id -g)], '<', \undef, '>', \$out);
|
||||
is($out, `id -g`);
|
||||
|
||||
done_testing;
|
|
@ -1,36 +0,0 @@
|
|||
Tests:
|
||||
basic
|
||||
dev
|
||||
net
|
||||
upstream
|
||||
userns
|
||||
Restrictions: allow-stderr, isolation-machine
|
||||
Depends:
|
||||
bubblewrap,
|
||||
iproute2:native,
|
||||
libcap2-bin:native,
|
||||
libipc-run-perl:native,
|
||||
perl:native,
|
||||
python3:native,
|
||||
|
||||
Tests: upstream-usrmerge
|
||||
Restrictions: allow-stderr, isolation-machine, breaks-testbed
|
||||
Depends:
|
||||
bubblewrap,
|
||||
iproute2:native,
|
||||
libcap2-bin:native,
|
||||
libipc-run-perl:native,
|
||||
perl:native,
|
||||
python3:native,
|
||||
usrmerge
|
||||
|
||||
Tests:
|
||||
upstream-as-root
|
||||
Restrictions: allow-stderr, isolation-machine, needs-root
|
||||
Depends:
|
||||
bubblewrap,
|
||||
iproute2:native,
|
||||
libcap2-bin:native,
|
||||
libipc-run-perl:native,
|
||||
perl:native,
|
||||
python3:native,
|
|
@ -1,40 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# vim:set sw=4 sts=4 et ft=perl:
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use IPC::Run qw(run);
|
||||
|
||||
sub run_ok {
|
||||
my $argv = shift;
|
||||
my $debug = join(' ', @$argv);
|
||||
ok(run($argv, @_), qq{"$debug" should succeed});
|
||||
}
|
||||
|
||||
my $out;
|
||||
run_ok([qw(bwrap --ro-bind / / --dev /dev //bin/sh -c), "echo /dev/*"],
|
||||
'<', \undef, '>', \$out);
|
||||
like($out, qr{(^| )/dev/full( |$)});
|
||||
like($out, qr{(^| )/dev/null( |$)});
|
||||
like($out, qr{(^| )/dev/pts( |$)});
|
||||
like($out, qr{(^| )/dev/random( |$)});
|
||||
like($out, qr{(^| )/dev/shm( |$)});
|
||||
like($out, qr{(^| )/dev/stderr( |$)});
|
||||
like($out, qr{(^| )/dev/stdin( |$)});
|
||||
like($out, qr{(^| )/dev/stdout( |$)});
|
||||
like($out, qr{(^| )/dev/tty( |$)});
|
||||
like($out, qr{(^| )/dev/urandom( |$)});
|
||||
like($out, qr{(^| )/dev/zero( |$)});
|
||||
unlike($out, qr{(^| )/dev/hda( |$)});
|
||||
unlike($out, qr{(^| )/dev/dsp( |$)});
|
||||
unlike($out, qr{(^| )/dev/fuse( |$)});
|
||||
unlike($out, qr{(^| )/dev/kmsg( |$)});
|
||||
unlike($out, qr{(^| )/dev/loop0( |$)});
|
||||
unlike($out, qr{(^| )/dev/mem( |$)});
|
||||
unlike($out, qr{(^| )/dev/sda( |$)});
|
||||
unlike($out, qr{(^| )/dev/snd( |$)});
|
||||
unlike($out, qr{(^| )/dev/tty1( |$)});
|
||||
unlike($out, qr{(^| )/dev/vda( |$)});
|
||||
|
||||
done_testing;
|
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# vim:set sw=4 sts=4 et ft=perl:
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use IPC::Run qw(run);
|
||||
|
||||
sub run_ok {
|
||||
my $argv = shift;
|
||||
my $debug = join(' ', @$argv);
|
||||
ok(run($argv, @_), qq{"$debug" should succeed});
|
||||
}
|
||||
|
||||
my $out;
|
||||
run_ok([qw(bwrap --ro-bind / / --unshare-net /bin/sh -c), "ip link ls"],
|
||||
'<', \undef, '>', \$out);
|
||||
|
||||
like($out, qr{^[0-9]+: lo:});
|
||||
unlike($out, qr{^[0-9]+: en[^:]*:});
|
||||
unlike($out, qr{^[0-9]+: eth[^:]*:});
|
||||
unlike($out, qr{^[0-9]+: wlan[^:]*:});
|
||||
|
||||
done_testing;
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
exec tests/test-run.sh
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
exec tests/test-run.sh
|
|
@ -1 +0,0 @@
|
|||
upstream
|
|
@ -1,42 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# vim:set sw=4 sts=4 et ft=perl:
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use IPC::Run qw(run);
|
||||
|
||||
sub run_ok {
|
||||
my $argv = shift;
|
||||
my $debug = join(' ', @$argv);
|
||||
ok(run($argv, @_), qq{"$debug" should succeed});
|
||||
}
|
||||
|
||||
my $out;
|
||||
|
||||
diag("Unshare user ID");
|
||||
run_ok([qw(bwrap --ro-bind / / --unshare-user --uid 2 --gid 3 /usr/bin/id -u)],
|
||||
'<', \undef, '>', \$out);
|
||||
is($out, "2\n");
|
||||
run_ok([qw(bwrap --ro-bind / / --unshare-user --uid 2 --gid 3 /usr/bin/id -g)],
|
||||
'<', \undef, '>', \$out);
|
||||
is($out, "3\n");
|
||||
run_ok([qw(bwrap --ro-bind / / --unshare-user --uid 2 --gid 3 /bin/sh -c),
|
||||
'ls -l /etc/passwd'],
|
||||
'<', \undef, '>', \$out);
|
||||
like($out, qr{ nobody nogroup });
|
||||
|
||||
diag("Combine new /dev with new user namespace (#71)");
|
||||
run_ok([qw(bwrap --ro-bind / / --unshare-user --uid 2 --gid 3 --dev /dev /bin/sh -c),
|
||||
'echo /dev/*'],
|
||||
'<', \undef, '>', \$out);
|
||||
like($out, qr{(^| )/dev/full( |$)});
|
||||
unlike($out, qr{(^| )/dev/tty1( |$)});
|
||||
run_ok([qw(bwrap --ro-bind / / --unshare-user --uid 2 --gid 3 --dev /dev /usr/bin/id -u)],
|
||||
'<', \undef, '>', \$out);
|
||||
is($out, "2\n");
|
||||
run_ok([qw(bwrap --ro-bind / / --unshare-user --uid 2 --gid 3 --dev /dev /usr/bin/id -g)],
|
||||
'<', \undef, '>', \$out);
|
||||
is($out, "3\n");
|
||||
|
||||
done_testing;
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
Name: Bubblewrap
|
||||
Repository: https://github.com/projectatomic/bubblewrap
|
||||
Repository-Browse: https://github.com/projectatomic/bubblewrap
|
||||
Bug-Database: https://github.com/projectatomic/bubblewrap/issues
|
||||
Bug-Submit: https://github.com/projectatomic/bubblewrap/issues/new
|
||||
...
|
||||
# vim:set ft=yaml:
|
|
@ -1,3 +0,0 @@
|
|||
version=4
|
||||
opts="compression=xz,dversionmangle=s/\+(?:git)?[0-9]*\+g[0-9a-f]*//" \
|
||||
https://github.com/projectatomic/@PACKAGE@/releases .*/@PACKAGE@-@ANY_VERSION@@ARCHIVE_EXT@
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# For this to work you first have to run these commands:
|
||||
# curl -O http://sdk.gnome.org/nightly/keys/nightly.gpg
|
||||
# flatpak --user remote-add --gpg-key=nightly.gpg gnome-nightly http://sdk.gnome.org/nightly/repo/
|
||||
|
|
|
@ -24,9 +24,8 @@ else:
|
|||
os.close(pipe_info[0])
|
||||
os.close(userns_block[1])
|
||||
|
||||
if sys.version_info >= (3, 4):
|
||||
os.set_inheritable(pipe_info[1], True)
|
||||
os.set_inheritable(userns_block[0], True)
|
||||
os.set_inheritable(pipe_info[1], True)
|
||||
os.set_inheritable(userns_block[0], True)
|
||||
|
||||
args = ["bwrap",
|
||||
"bwrap",
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
# git.mk, a small Makefile to autogenerate .gitignore files
|
||||
# for autotools-based projects.
|
||||
#
|
||||
# Copyright 2009, Red Hat, Inc.
|
||||
# Copyright 2010,2011,2012,2013 Behdad Esfahbod
|
||||
# Written by Behdad Esfahbod
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# is permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved.
|
||||
#
|
||||
# The latest version of this file can be downloaded from:
|
||||
GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk
|
||||
#
|
||||
# Bugs, etc, should be reported upstream at:
|
||||
# https://github.com/behdad/git.mk
|
||||
#
|
||||
# To use in your project, import this file in your git repo's toplevel,
|
||||
# then do "make -f git.mk". This modifies all Makefile.am files in
|
||||
# your project to -include git.mk. Remember to add that line to new
|
||||
# Makefile.am files you create in your project, or just rerun the
|
||||
# "make -f git.mk".
|
||||
#
|
||||
# This enables automatic .gitignore generation. If you need to ignore
|
||||
# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
|
||||
# But think twice before doing that. If a file has to be in .gitignore,
|
||||
# chances are very high that it's a generated file and should be in one
|
||||
# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
|
||||
#
|
||||
# The only case that you need to manually add a file to GITIGNOREFILES is
|
||||
# when remove files in one of mostlyclean-local, clean-local, distclean-local,
|
||||
# or maintainer-clean-local make targets.
|
||||
#
|
||||
# Note that for files like editor backup, etc, there are better places to
|
||||
# ignore them. See "man gitignore".
|
||||
#
|
||||
# If "make maintainer-clean" removes the files but they are not recognized
|
||||
# by this script (that is, if "git status" shows untracked files still), send
|
||||
# me the output of "git status" as well as your Makefile.am and Makefile for
|
||||
# the directories involved and I'll diagnose.
|
||||
#
|
||||
# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
|
||||
# Makefile.am.sample in the git.mk git repo.
|
||||
#
|
||||
# Don't EXTRA_DIST this file. It is supposed to only live in git clones,
|
||||
# not tarballs. It serves no useful purpose in tarballs and clutters the
|
||||
# build dir.
|
||||
#
|
||||
# This file knows how to handle autoconf, automake, libtool, gtk-doc,
|
||||
# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata,
|
||||
# appstream.
|
||||
#
|
||||
# This makefile provides the following targets:
|
||||
#
|
||||
# - all: "make all" will build all gitignore files.
|
||||
# - gitignore: makes all gitignore files in the current dir and subdirs.
|
||||
# - .gitignore: make gitignore file for the current dir.
|
||||
# - gitignore-recurse: makes all gitignore files in the subdirs.
|
||||
#
|
||||
# KNOWN ISSUES:
|
||||
#
|
||||
# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
|
||||
# submodule doesn't find us. If you have configure.{in,ac} files in
|
||||
# subdirs, add a proxy git.mk file in those dirs that simply does:
|
||||
# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste.
|
||||
# And add those files to git. See vte/gnome-pty-helper/git.mk for
|
||||
# example.
|
||||
#
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES:
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
# Most autotools-using modules should be fine including this variable in their
|
||||
# toplevel MAINTAINERCLEANFILES:
|
||||
GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \
|
||||
$(srcdir)/aclocal.m4 \
|
||||
$(srcdir)/autoscan.log \
|
||||
$(srcdir)/configure.scan \
|
||||
`AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \
|
||||
test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \
|
||||
for x in \
|
||||
ar-lib \
|
||||
compile \
|
||||
config.guess \
|
||||
config.sub \
|
||||
depcomp \
|
||||
install-sh \
|
||||
ltmain.sh \
|
||||
missing \
|
||||
mkinstalldirs \
|
||||
test-driver \
|
||||
ylwrap \
|
||||
; do echo "$$AUX_DIR/$$x"; done` \
|
||||
`cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \
|
||||
head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done`
|
||||
#
|
||||
# All modules should also be fine including the following variable, which
|
||||
# removes automake-generated Makefile.in files:
|
||||
GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \
|
||||
`cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \
|
||||
while read f; do \
|
||||
case $$f in Makefile|*/Makefile) \
|
||||
test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \
|
||||
done`
|
||||
#
|
||||
# Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this,
|
||||
# though it's harmless to include regardless.
|
||||
GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \
|
||||
`MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \
|
||||
if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \
|
||||
for x in \
|
||||
libtool.m4 \
|
||||
ltoptions.m4 \
|
||||
ltsugar.m4 \
|
||||
ltversion.m4 \
|
||||
lt~obsolete.m4 \
|
||||
; do echo "$$MACRO_DIR/$$x"; done; \
|
||||
fi`
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Default rule is to install ourselves in all Makefile.am files:
|
||||
###############################################################################
|
||||
|
||||
git-all: git-mk-install
|
||||
|
||||
git-mk-install:
|
||||
@echo "Installing git makefile"
|
||||
@any_failed=; \
|
||||
find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \
|
||||
if grep 'include .*/git.mk' $$x >/dev/null; then \
|
||||
echo "$$x already includes git.mk"; \
|
||||
else \
|
||||
failed=; \
|
||||
echo "Updating $$x"; \
|
||||
{ cat $$x; \
|
||||
echo ''; \
|
||||
echo '-include $$(top_srcdir)/git.mk'; \
|
||||
} > $$x.tmp || failed=1; \
|
||||
if test x$$failed = x; then \
|
||||
mv $$x.tmp $$x || failed=1; \
|
||||
fi; \
|
||||
if test x$$failed = x; then : else \
|
||||
echo "Failed updating $$x"; >&2 \
|
||||
any_failed=1; \
|
||||
fi; \
|
||||
fi; done; test -z "$$any_failed"
|
||||
|
||||
git-mk-update:
|
||||
wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk
|
||||
|
||||
.PHONY: git-all git-mk-install git-mk-update
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Actual .gitignore generation:
|
||||
###############################################################################
|
||||
|
||||
$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
|
||||
@echo "git.mk: Generating $@"
|
||||
@{ \
|
||||
if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
|
||||
for x in \
|
||||
$(DOC_MODULE)-decl-list.txt \
|
||||
$(DOC_MODULE)-decl.txt \
|
||||
tmpl/$(DOC_MODULE)-unused.sgml \
|
||||
"tmpl/*.bak" \
|
||||
$(REPORT_FILES) \
|
||||
$(DOC_MODULE).pdf \
|
||||
xml html \
|
||||
; do echo "/$$x"; done; \
|
||||
FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \
|
||||
case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \
|
||||
if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \
|
||||
echo "/$(DOC_MODULE).types"; \
|
||||
fi; \
|
||||
if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \
|
||||
echo "/$(DOC_MODULE)-sections.txt"; \
|
||||
fi; \
|
||||
if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
|
||||
for x in \
|
||||
$(SETUP_FILES) \
|
||||
$(DOC_MODULE).types \
|
||||
; do echo "/$$x"; done; \
|
||||
fi; \
|
||||
fi; \
|
||||
if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
|
||||
for lc in $(DOC_LINGUAS); do \
|
||||
for x in \
|
||||
$(if $(DOC_MODULE),$(DOC_MODULE).xml) \
|
||||
$(DOC_PAGES) \
|
||||
$(DOC_INCLUDES) \
|
||||
; do echo "/$$lc/$$x"; done; \
|
||||
done; \
|
||||
for x in \
|
||||
$(_DOC_OMF_ALL) \
|
||||
$(_DOC_DSK_ALL) \
|
||||
$(_DOC_HTML_ALL) \
|
||||
$(_DOC_MOFILES) \
|
||||
$(DOC_H_FILE) \
|
||||
"*/.xml2po.mo" \
|
||||
"*/*.omf.out" \
|
||||
; do echo /$$x; done; \
|
||||
fi; \
|
||||
if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \
|
||||
for lc in $(HELP_LINGUAS); do \
|
||||
for x in \
|
||||
$(HELP_FILES) \
|
||||
"$$lc.stamp" \
|
||||
"$$lc.mo" \
|
||||
; do echo "/$$lc/$$x"; done; \
|
||||
done; \
|
||||
fi; \
|
||||
if test "x$(gsettings_SCHEMAS)" = x; then :; else \
|
||||
for x in \
|
||||
$(gsettings_SCHEMAS:.xml=.valid) \
|
||||
$(gsettings__enum_file) \
|
||||
; do echo "/$$x"; done; \
|
||||
fi; \
|
||||
if test "x$(appdata_XML)" = x; then :; else \
|
||||
for x in \
|
||||
$(appdata_XML:.xml=.valid) \
|
||||
; do echo "/$$x"; done; \
|
||||
fi; \
|
||||
if test "x$(appstream_XML)" = x; then :; else \
|
||||
for x in \
|
||||
$(appstream_XML:.xml=.valid) \
|
||||
; do echo "/$$x"; done; \
|
||||
fi; \
|
||||
if test -f $(srcdir)/po/Makefile.in.in; then \
|
||||
for x in \
|
||||
po/Makefile.in.in \
|
||||
po/Makefile.in.in~ \
|
||||
po/Makefile.in \
|
||||
po/Makefile \
|
||||
po/Makevars.template \
|
||||
po/POTFILES \
|
||||
po/Rules-quot \
|
||||
po/stamp-it \
|
||||
po/stamp-po \
|
||||
po/.intltool-merge-cache \
|
||||
"po/*.gmo" \
|
||||
"po/*.header" \
|
||||
"po/*.mo" \
|
||||
"po/*.sed" \
|
||||
"po/*.sin" \
|
||||
po/$(GETTEXT_PACKAGE).pot \
|
||||
intltool-extract.in \
|
||||
intltool-merge.in \
|
||||
intltool-update.in \
|
||||
; do echo "/$$x"; done; \
|
||||
fi; \
|
||||
if test -f $(srcdir)/configure; then \
|
||||
for x in \
|
||||
autom4te.cache \
|
||||
configure \
|
||||
config.h \
|
||||
stamp-h1 \
|
||||
libtool \
|
||||
config.lt \
|
||||
; do echo "/$$x"; done; \
|
||||
fi; \
|
||||
if test "x$(DEJATOOL)" = x; then :; else \
|
||||
for x in \
|
||||
$(DEJATOOL) \
|
||||
; do echo "/$$x.sum"; echo "/$$x.log"; done; \
|
||||
echo /site.exp; \
|
||||
fi; \
|
||||
if test "x$(am__dirstamp)" = x; then :; else \
|
||||
echo "$(am__dirstamp)"; \
|
||||
fi; \
|
||||
if test "x$(LTCOMPILE)" = x -a "x$(LTCXXCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \
|
||||
for x in \
|
||||
"*.lo" \
|
||||
".libs" "_libs" \
|
||||
; do echo "$$x"; done; \
|
||||
fi; \
|
||||
for x in \
|
||||
.gitignore \
|
||||
$(GITIGNOREFILES) \
|
||||
$(CLEANFILES) \
|
||||
$(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \
|
||||
$(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \
|
||||
$(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \
|
||||
so_locations \
|
||||
$(MOSTLYCLEANFILES) \
|
||||
$(TEST_LOGS) \
|
||||
$(TEST_LOGS:.log=.trs) \
|
||||
$(TEST_SUITE_LOG) \
|
||||
$(TESTS:=.test) \
|
||||
"*.gcda" \
|
||||
"*.gcno" \
|
||||
$(DISTCLEANFILES) \
|
||||
$(am__CONFIG_DISTCLEAN_FILES) \
|
||||
$(CONFIG_CLEAN_FILES) \
|
||||
TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
|
||||
"*.tab.c" \
|
||||
$(MAINTAINERCLEANFILES) \
|
||||
$(BUILT_SOURCES) \
|
||||
$(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \
|
||||
$(filter %_vala.stamp,$(DIST_COMMON)) \
|
||||
$(filter %.vapi,$(DIST_COMMON)) \
|
||||
$(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \
|
||||
Makefile \
|
||||
Makefile.in \
|
||||
"*.orig" \
|
||||
"*.rej" \
|
||||
"*.bak" \
|
||||
"*~" \
|
||||
".*.sw[nop]" \
|
||||
".dirstamp" \
|
||||
; do echo "/$$x"; done; \
|
||||
for x in \
|
||||
"*.$(OBJEXT)" \
|
||||
$(DEPDIR) \
|
||||
; do echo "$$x"; done; \
|
||||
} | \
|
||||
sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
|
||||
sed 's@/[.]/@/@g' | \
|
||||
LC_ALL=C sort | uniq > $@.tmp && \
|
||||
mv $@.tmp $@;
|
||||
|
||||
all: $(srcdir)/.gitignore gitignore-recurse-maybe
|
||||
gitignore: $(srcdir)/.gitignore gitignore-recurse
|
||||
|
||||
gitignore-recurse-maybe:
|
||||
@for subdir in $(DIST_SUBDIRS); do \
|
||||
case " $(SUBDIRS) " in \
|
||||
*" $$subdir "*) :;; \
|
||||
*) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \
|
||||
esac; \
|
||||
done
|
||||
gitignore-recurse:
|
||||
@for subdir in $(DIST_SUBDIRS); do \
|
||||
test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \
|
||||
done
|
||||
|
||||
maintainer-clean: gitignore-clean
|
||||
gitignore-clean:
|
||||
-rm -f $(srcdir)/.gitignore
|
||||
|
||||
.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe
|
|
@ -0,0 +1,171 @@
|
|||
project(
|
||||
'bubblewrap',
|
||||
'c',
|
||||
version : '0.9.0',
|
||||
meson_version : '>=0.49.0',
|
||||
default_options : [
|
||||
'warning_level=2',
|
||||
],
|
||||
)
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
add_project_arguments('-D_GNU_SOURCE', language : 'c')
|
||||
common_include_directories = include_directories('.')
|
||||
|
||||
# Keep this in sync with ostree, except remove -Wall (part of Meson
|
||||
# warning_level 2) and -Werror=declaration-after-statement
|
||||
add_project_arguments(
|
||||
cc.get_supported_arguments([
|
||||
'-Werror=shadow',
|
||||
'-Werror=empty-body',
|
||||
'-Werror=strict-prototypes',
|
||||
'-Werror=missing-prototypes',
|
||||
'-Werror=implicit-function-declaration',
|
||||
'-Werror=pointer-arith',
|
||||
'-Werror=init-self',
|
||||
'-Werror=missing-declarations',
|
||||
'-Werror=return-type',
|
||||
'-Werror=overflow',
|
||||
'-Werror=int-conversion',
|
||||
'-Werror=parenthesis',
|
||||
'-Werror=incompatible-pointer-types',
|
||||
'-Werror=misleading-indentation',
|
||||
'-Werror=missing-include-dirs',
|
||||
'-Werror=aggregate-return',
|
||||
|
||||
# Extra warnings specific to bubblewrap
|
||||
'-Werror=switch-default',
|
||||
'-Wswitch-enum',
|
||||
|
||||
# Deliberately not warning about these, ability to zero-initialize
|
||||
# a struct is a feature
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-error=missing-field-initializers',
|
||||
]),
|
||||
language : 'c',
|
||||
)
|
||||
|
||||
if (
|
||||
cc.has_argument('-Werror=format=2')
|
||||
and cc.has_argument('-Werror=format-security')
|
||||
and cc.has_argument('-Werror=format-nonliteral')
|
||||
)
|
||||
add_project_arguments([
|
||||
'-Werror=format=2',
|
||||
'-Werror=format-security',
|
||||
'-Werror=format-nonliteral',
|
||||
], language : 'c')
|
||||
endif
|
||||
|
||||
bash = find_program('bash', required : false)
|
||||
|
||||
if get_option('python') == ''
|
||||
python = find_program('python3')
|
||||
else
|
||||
python = find_program(get_option('python'))
|
||||
endif
|
||||
|
||||
libcap_dep = dependency('libcap', required : true)
|
||||
|
||||
selinux_dep = dependency(
|
||||
'libselinux',
|
||||
version : '>=2.1.9',
|
||||
# if disabled, Meson will behave as though libselinux was not found
|
||||
required : get_option('selinux'),
|
||||
)
|
||||
|
||||
cdata = configuration_data()
|
||||
cdata.set_quoted(
|
||||
'PACKAGE_STRING',
|
||||
'@0@ @1@'.format(meson.project_name(), meson.project_version()),
|
||||
)
|
||||
|
||||
if selinux_dep.found()
|
||||
cdata.set('HAVE_SELINUX', 1)
|
||||
if selinux_dep.version().version_compare('>=2.3')
|
||||
cdata.set('HAVE_SELINUX_2_3', 1)
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('require_userns')
|
||||
cdata.set('ENABLE_REQUIRE_USERNS', 1)
|
||||
endif
|
||||
|
||||
configure_file(
|
||||
output : 'config.h',
|
||||
configuration : cdata,
|
||||
)
|
||||
|
||||
if meson.is_subproject() and get_option('program_prefix') == ''
|
||||
error('program_prefix option must be set when bwrap is a subproject')
|
||||
endif
|
||||
|
||||
if get_option('bwrapdir') != ''
|
||||
bwrapdir = get_option('bwrapdir')
|
||||
elif meson.is_subproject()
|
||||
bwrapdir = get_option('libexecdir')
|
||||
else
|
||||
bwrapdir = get_option('bindir')
|
||||
endif
|
||||
|
||||
bwrap = executable(
|
||||
get_option('program_prefix') + 'bwrap',
|
||||
[
|
||||
'bubblewrap.c',
|
||||
'bind-mount.c',
|
||||
'network.c',
|
||||
'utils.c',
|
||||
],
|
||||
build_rpath : get_option('build_rpath'),
|
||||
install : true,
|
||||
install_dir : bwrapdir,
|
||||
install_rpath : get_option('install_rpath'),
|
||||
dependencies : [selinux_dep, libcap_dep],
|
||||
)
|
||||
|
||||
manpages_xsl = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
|
||||
xsltproc = find_program('xsltproc', required : get_option('man'))
|
||||
build_man_page = false
|
||||
|
||||
if xsltproc.found() and not meson.is_subproject()
|
||||
if run_command([
|
||||
xsltproc, '--nonet', manpages_xsl,
|
||||
], check : false).returncode() == 0
|
||||
message('Docbook XSL found, man page enabled')
|
||||
build_man_page = true
|
||||
elif get_option('man').enabled()
|
||||
error('Man page requested, but Docbook XSL stylesheets not found')
|
||||
else
|
||||
message('Docbook XSL not found, man page disabled automatically')
|
||||
endif
|
||||
endif
|
||||
|
||||
if build_man_page
|
||||
custom_target(
|
||||
'bwrap.1',
|
||||
output : 'bwrap.1',
|
||||
input : 'bwrap.xml',
|
||||
command : [
|
||||
xsltproc,
|
||||
'--nonet',
|
||||
'--stringparam', 'man.output.quietly', '1',
|
||||
'--stringparam', 'funcsynopsis.style', 'ansi',
|
||||
'--stringparam', 'man.th.extra1.suppress', '1',
|
||||
'--stringparam', 'man.authors.section.enabled', '0',
|
||||
'--stringparam', 'man.copyright.section.enabled', '0',
|
||||
'-o', '@OUTPUT@',
|
||||
manpages_xsl,
|
||||
'@INPUT@',
|
||||
],
|
||||
install : true,
|
||||
install_dir : get_option('mandir') / 'man1',
|
||||
)
|
||||
endif
|
||||
|
||||
if not meson.is_subproject()
|
||||
subdir('completions')
|
||||
endif
|
||||
|
||||
if get_option('tests')
|
||||
subdir('tests')
|
||||
endif
|
|
@ -0,0 +1,73 @@
|
|||
option(
|
||||
'bash_completion',
|
||||
type : 'feature',
|
||||
description : 'install bash completion script',
|
||||
value : 'enabled',
|
||||
)
|
||||
option(
|
||||
'bash_completion_dir',
|
||||
type : 'string',
|
||||
description : 'install bash completion script in this directory',
|
||||
value : '',
|
||||
)
|
||||
option(
|
||||
'bwrapdir',
|
||||
type : 'string',
|
||||
description : 'install bwrap in this directory [default: bindir, or libexecdir in subprojects]',
|
||||
)
|
||||
option(
|
||||
'build_rpath',
|
||||
type : 'string',
|
||||
description : 'set a RUNPATH or RPATH on the bwrap executable',
|
||||
)
|
||||
option(
|
||||
'install_rpath',
|
||||
type : 'string',
|
||||
description : 'set a RUNPATH or RPATH on the bwrap executable',
|
||||
)
|
||||
option(
|
||||
'man',
|
||||
type : 'feature',
|
||||
description : 'generate man pages',
|
||||
value : 'auto',
|
||||
)
|
||||
option(
|
||||
'program_prefix',
|
||||
type : 'string',
|
||||
description : 'Prepend string to bwrap executable name, for use with subprojects',
|
||||
)
|
||||
option(
|
||||
'python',
|
||||
type : 'string',
|
||||
description : 'Path to Python 3, or empty to use python3',
|
||||
)
|
||||
option(
|
||||
'require_userns',
|
||||
type : 'boolean',
|
||||
description : 'require user namespaces by default when installed setuid',
|
||||
value : 'false',
|
||||
)
|
||||
option(
|
||||
'selinux',
|
||||
type : 'feature',
|
||||
description : 'enable optional SELINUX support',
|
||||
value : 'auto',
|
||||
)
|
||||
option(
|
||||
'tests',
|
||||
type : 'boolean',
|
||||
description : 'build tests',
|
||||
value : 'true',
|
||||
)
|
||||
option(
|
||||
'zsh_completion',
|
||||
type : 'feature',
|
||||
description : 'install zsh completion script',
|
||||
value : 'enabled',
|
||||
)
|
||||
option(
|
||||
'zsh_completion_dir',
|
||||
type : 'string',
|
||||
description : 'install zsh completion script in this directory',
|
||||
value : '',
|
||||
)
|
|
@ -1,5 +1,6 @@
|
|||
/* bubblewrap
|
||||
* Copyright (C) 2016 Alexander Larsson
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -61,8 +62,8 @@ rtnl_send_request (int rtnl_fd,
|
|||
}
|
||||
|
||||
static int
|
||||
rtnl_read_reply (int rtnl_fd,
|
||||
int seq_nr)
|
||||
rtnl_read_reply (int rtnl_fd,
|
||||
unsigned int seq_nr)
|
||||
{
|
||||
char buffer[1024];
|
||||
ssize_t received;
|
||||
|
@ -79,7 +80,7 @@ rtnl_read_reply (int rtnl_fd,
|
|||
{
|
||||
if (rheader->nlmsg_seq != seq_nr)
|
||||
return -1;
|
||||
if (rheader->nlmsg_pid != getpid ())
|
||||
if ((pid_t)rheader->nlmsg_pid != getpid ())
|
||||
return -1;
|
||||
if (rheader->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
|
@ -129,7 +130,7 @@ rtnl_setup_request (char *buffer,
|
|||
header->nlmsg_seq = counter++;
|
||||
header->nlmsg_pid = getpid ();
|
||||
|
||||
return (struct nlmsghdr *) header;
|
||||
return header;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* bubblewrap
|
||||
* Copyright (C) 2016 Alexander Larsson
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
|
|
@ -46,4 +46,3 @@ find $RPM_BUILD_ROOT -name '*.la' -delete
|
|||
%{_bindir}/bwrap
|
||||
%endif
|
||||
%{_mandir}/man1/*
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
bubblewrap release checklist
|
||||
============================
|
||||
|
||||
* Collect release notes
|
||||
* Update version number in `configure.ac` **and** `meson.build`
|
||||
* Commit the changes
|
||||
* `meson dist -C ${builddir}`
|
||||
* Do any final smoke-testing, e.g. update a package, install and test it
|
||||
* `git evtag sign v$VERSION`
|
||||
* Include the release notes in the tag message
|
||||
* `git push --atomic origin main v$VERSION`
|
||||
* https://github.com/containers/bubblewrap/releases/new
|
||||
* Fill in the new version's tag in the "Tag version" box
|
||||
* Title: `$VERSION`
|
||||
* Copy the release notes into the description
|
||||
* Upload the tarball that you built with `meson dist`
|
||||
* Get the `sha256sum` of the tarball and append it to the description
|
||||
* `Publish release`
|
|
@ -5,10 +5,13 @@
|
|||
#
|
||||
# Known copies are in the following repos:
|
||||
#
|
||||
# - https://github.com/projectatomic/rpm-ostree
|
||||
# - https://github.com/containers/bubblewrap
|
||||
# - https://github.com/coreos/rpm-ostree
|
||||
#
|
||||
# Copyright (C) 2017 Colin Walters <walters@verbum.org>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
|
@ -33,13 +36,19 @@ assert_not_reached () {
|
|||
}
|
||||
|
||||
# Some tests look for specific English strings. Use a UTF-8 version
|
||||
# of the C (POSIX) locale if we have one, or fall back to POSIX
|
||||
# of the C (POSIX) locale if we have one, or fall back to en_US.UTF-8
|
||||
# (https://sourceware.org/glibc/wiki/Proposals/C.UTF-8)
|
||||
if locale -a | grep C.UTF-8 >/dev/null; then
|
||||
export LC_ALL=C.UTF-8
|
||||
#
|
||||
# If we can't find the locale command assume we have support for C.UTF-8
|
||||
# (e.g. musl based systems)
|
||||
if type -p locale >/dev/null; then
|
||||
export LC_ALL=$(locale -a | grep -iEe '^(C|en_US)\.(UTF-8|utf8)$' | head -n1 || true)
|
||||
if [ -z "${LC_ALL}" ]; then fatal "Can't find suitable UTF-8 locale"; fi
|
||||
else
|
||||
export LC_ALL=C
|
||||
export LC_ALL=C.UTF-8
|
||||
fi
|
||||
# A GNU extension, used whenever LC_ALL is not C
|
||||
unset LANGUAGE
|
||||
|
||||
# This should really be the default IMO
|
||||
export G_DEBUG=fatal-warnings
|
||||
|
@ -119,10 +128,23 @@ assert_file_has_content () {
|
|||
done
|
||||
}
|
||||
|
||||
assert_file_has_content_once () {
|
||||
fpath=$1
|
||||
shift
|
||||
for re in "$@"; do
|
||||
if ! test $(grep -e "$re" "$fpath" | wc -l) = "1"; then
|
||||
_fatal_print_file "$fpath" "File '$fpath' doesn't match regexp '$re' exactly once"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
assert_file_has_content_literal () {
|
||||
if ! grep -q -F -e "$2" "$1"; then
|
||||
_fatal_print_file "$1" "File '$1' doesn't match fixed string list '$2'"
|
||||
fi
|
||||
fpath=$1; shift
|
||||
for s in "$@"; do
|
||||
if ! grep -q -F -e "$s" "$fpath"; then
|
||||
_fatal_print_file "$fpath" "File '$fpath' doesn't match fixed string list '$s'"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
assert_file_has_mode () {
|
||||
|
@ -159,6 +181,10 @@ skip() {
|
|||
exit 0
|
||||
}
|
||||
|
||||
extract_child_pid() {
|
||||
grep child-pid "$1" | sed "s/^.*: \([0-9]*\).*/\1/"
|
||||
report_err () {
|
||||
local exit_status="$?"
|
||||
{ { local BASH_XTRACEFD=3; } 2> /dev/null
|
||||
echo "Unexpected nonzero exit status $exit_status while running: $BASH_COMMAND" >&2
|
||||
} 3> /dev/null
|
||||
}
|
||||
trap report_err ERR
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
# shellcheck shell=bash
|
||||
|
||||
# Source library for shell script tests.
|
||||
# Add non-bubblewrap-specific code to libtest-core.sh instead.
|
||||
#
|
||||
# Copyright (C) 2017 Colin Walters <walters@verbum.org>
|
||||
# SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
set -e
|
||||
|
||||
if [ -n "${G_TEST_SRCDIR:-}" ]; then
|
||||
test_srcdir="${G_TEST_SRCDIR}/tests"
|
||||
else
|
||||
test_srcdir=$(dirname "$0")
|
||||
fi
|
||||
|
||||
if [ -n "${G_TEST_BUILDDIR:-}" ]; then
|
||||
test_builddir="${G_TEST_BUILDDIR}/tests"
|
||||
else
|
||||
test_builddir=$(dirname "$0")
|
||||
fi
|
||||
|
||||
. "${test_srcdir}/libtest-core.sh"
|
||||
|
||||
# Make sure /sbin/getpcaps etc. are in our PATH even if non-root
|
||||
PATH="$PATH:/usr/sbin:/sbin"
|
||||
|
||||
tempdir=$(mktemp -d /var/tmp/tap-test.XXXXXX)
|
||||
touch "${tempdir}/.testtmp"
|
||||
cleanup() {
|
||||
if test -n "${TEST_SKIP_CLEANUP:-}"; then
|
||||
echo "Skipping cleanup of ${tempdir}"
|
||||
elif test -f "${tempdir}/.testtmp"; then
|
||||
rm -rf "${tempdir}"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
cd "${tempdir}"
|
||||
|
||||
: "${BWRAP:=bwrap}"
|
||||
if test -u "$(type -p ${BWRAP})"; then
|
||||
bwrap_is_suid=true
|
||||
fi
|
||||
|
||||
FUSE_DIR=
|
||||
for mp in $(grep " fuse[. ]" /proc/self/mounts | grep "user_id=$(id -u)" | awk '{print $2}'); do
|
||||
if test -d "$mp"; then
|
||||
echo "# Using $mp as test fuse mount"
|
||||
FUSE_DIR="$mp"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if test "$(id -u)" = "0"; then
|
||||
is_uidzero=true
|
||||
else
|
||||
is_uidzero=false
|
||||
fi
|
||||
|
||||
# This is supposed to be an otherwise readable file in an unreadable (by the user) dir
|
||||
UNREADABLE=/root/.bashrc
|
||||
if "${is_uidzero}" || test -x "$(dirname "$UNREADABLE")"; then
|
||||
UNREADABLE=
|
||||
fi
|
||||
|
||||
# https://github.com/projectatomic/bubblewrap/issues/217
|
||||
# are we on a merged-/usr system?
|
||||
if [ /lib -ef /usr/lib ]; then
|
||||
BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr
|
||||
--ro-bind /etc /etc
|
||||
--dir /var/tmp
|
||||
--symlink usr/lib /lib
|
||||
--symlink usr/lib64 /lib64
|
||||
--symlink usr/bin /bin
|
||||
--symlink usr/sbin /sbin
|
||||
--proc /proc
|
||||
--dev /dev"
|
||||
else
|
||||
BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr
|
||||
--ro-bind /etc /etc
|
||||
--ro-bind /bin /bin
|
||||
--ro-bind-try /lib /lib
|
||||
--ro-bind-try /lib64 /lib64
|
||||
--ro-bind-try /sbin /sbin
|
||||
--ro-bind-try /nix/store /nix/store
|
||||
--dir /var/tmp
|
||||
--proc /proc
|
||||
--dev /dev"
|
||||
fi
|
||||
|
||||
# Default arg, bind whole host fs to /, tmpfs on /tmp
|
||||
RUN="${BWRAP} --bind / / --tmpfs /tmp"
|
||||
|
||||
if [ -z "${BWRAP_MUST_WORK-}" ] && ! $RUN true; then
|
||||
skip Seems like bwrap is not working at all. Maybe setuid is not working
|
||||
fi
|
||||
|
||||
extract_child_pid() {
|
||||
grep child-pid "$1" | sed "s/^.*: \([0-9]*\).*/\1/"
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
test_programs = [
|
||||
['test-utils', executable(
|
||||
'test-utils',
|
||||
'test-utils.c',
|
||||
'../utils.c',
|
||||
'../utils.h',
|
||||
dependencies : [selinux_dep],
|
||||
include_directories : common_include_directories,
|
||||
)],
|
||||
]
|
||||
|
||||
executable(
|
||||
'try-syscall',
|
||||
'try-syscall.c',
|
||||
override_options: ['b_sanitize=none'],
|
||||
)
|
||||
|
||||
test_scripts = [
|
||||
'test-run.sh',
|
||||
'test-seccomp.py',
|
||||
'test-specifying-pidns.sh',
|
||||
'test-specifying-userns.sh',
|
||||
]
|
||||
|
||||
test_env = environment()
|
||||
test_env.set('BWRAP', bwrap.full_path())
|
||||
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir() / '..')
|
||||
test_env.set('G_TEST_SRCDIR', meson.current_source_dir() / '..')
|
||||
|
||||
foreach pair : test_programs
|
||||
name = pair[0]
|
||||
test_program = pair[1]
|
||||
if meson.version().version_compare('>=0.50.0')
|
||||
test(
|
||||
name,
|
||||
test_program,
|
||||
env : test_env,
|
||||
protocol : 'tap',
|
||||
)
|
||||
else
|
||||
test(
|
||||
name,
|
||||
test_program,
|
||||
env : test_env,
|
||||
)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
foreach test_script : test_scripts
|
||||
if test_script.endswith('.py')
|
||||
interpreter = python
|
||||
else
|
||||
interpreter = bash
|
||||
endif
|
||||
|
||||
if meson.version().version_compare('>=0.50.0')
|
||||
test(
|
||||
test_script,
|
||||
interpreter,
|
||||
args : [files(test_script)],
|
||||
env : test_env,
|
||||
protocol : 'tap',
|
||||
)
|
||||
else
|
||||
test(
|
||||
test_script,
|
||||
interpreter,
|
||||
args : [files(test_script)],
|
||||
env : test_env,
|
||||
)
|
||||
endif
|
||||
endforeach
|
|
@ -1,106 +1,44 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
# Make sure /sbin/getpcaps etc. are in our PATH even if non-root
|
||||
PATH="$PATH:/usr/sbin:/sbin"
|
||||
srcd=$(cd $(dirname "$0") && pwd)
|
||||
|
||||
srcd=$(cd $(dirname $0) && pwd)
|
||||
. ${srcd}/libtest.sh
|
||||
|
||||
. ${srcd}/libtest-core.sh
|
||||
bn=$(basename "$0")
|
||||
|
||||
bn=$(basename $0)
|
||||
tempdir=$(mktemp -d /var/tmp/tap-test.XXXXXX)
|
||||
touch ${tempdir}/.testtmp
|
||||
function cleanup () {
|
||||
if test -n "${TEST_SKIP_CLEANUP:-}"; then
|
||||
echo "Skipping cleanup of ${test_tmpdir}"
|
||||
else if test -f ${tempdir}/.test; then
|
||||
rm "${tempdir}" -rf
|
||||
fi
|
||||
fi
|
||||
test_count=0
|
||||
ok () {
|
||||
test_count=$((test_count + 1))
|
||||
echo ok $test_count "$@"
|
||||
}
|
||||
ok_skip () {
|
||||
ok "# SKIP" "$@"
|
||||
}
|
||||
done_testing () {
|
||||
echo "1..$test_count"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
cd ${tempdir}
|
||||
|
||||
: "${BWRAP:=bwrap}"
|
||||
if test -u "$(type -p ${BWRAP})"; then
|
||||
bwrap_is_suid=true
|
||||
fi
|
||||
|
||||
FUSE_DIR=
|
||||
for mp in $(cat /proc/self/mounts | grep " fuse[. ]" | grep user_id=$(id -u) | awk '{print $2}'); do
|
||||
if test -d $mp; then
|
||||
echo Using $mp as test fuse mount
|
||||
FUSE_DIR=$mp
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if test "$(id -u)" = "0"; then
|
||||
is_uidzero=true
|
||||
else
|
||||
is_uidzero=false
|
||||
fi
|
||||
|
||||
# This is supposed to be an otherwise readable file in an unreadable (by the user) dir
|
||||
UNREADABLE=/root/.bashrc
|
||||
if ${is_uidzero} || test -x `dirname $UNREADABLE`; then
|
||||
UNREADABLE=
|
||||
fi
|
||||
|
||||
# https://github.com/projectatomic/bubblewrap/issues/217
|
||||
# are we on a merged-/usr system?
|
||||
if [ /lib -ef /usr/lib ]; then
|
||||
BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr
|
||||
--ro-bind /etc /etc
|
||||
--dir /var/tmp
|
||||
--symlink usr/lib /lib
|
||||
--symlink usr/lib64 /lib64
|
||||
--symlink usr/bin /bin
|
||||
--symlink usr/sbin /sbin
|
||||
--proc /proc
|
||||
--dev /dev"
|
||||
else
|
||||
BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr
|
||||
--ro-bind /etc /etc
|
||||
--ro-bind /bin /bin
|
||||
--ro-bind /lib /lib
|
||||
--ro-bind-try /lib64 /lib64
|
||||
--ro-bind /sbin /sbin
|
||||
--dir /var/tmp
|
||||
--proc /proc
|
||||
--dev /dev"
|
||||
fi
|
||||
|
||||
# Default arg, bind whole host fs to /, tmpfs on /tmp
|
||||
RUN="${BWRAP} --bind / / --tmpfs /tmp"
|
||||
|
||||
if ! $RUN true; then
|
||||
skip Seems like bwrap is not working at all. Maybe setuid is not working
|
||||
fi
|
||||
|
||||
echo "1..49"
|
||||
|
||||
# Test help
|
||||
${BWRAP} --help > help.txt
|
||||
assert_file_has_content help.txt "usage: ${BWRAP}"
|
||||
echo "ok - Help works"
|
||||
ok "Help works"
|
||||
|
||||
for ALT in "" "--unshare-user-try" "--unshare-pid" "--unshare-user-try --unshare-pid"; do
|
||||
for ALT in "" "--unshare-user-try" "--unshare-pid" "--unshare-user-try --unshare-pid"; do
|
||||
# Test fuse fs as bind source
|
||||
if [ x$FUSE_DIR != x ]; then
|
||||
$RUN $ALT --proc /proc --dev /dev --bind $FUSE_DIR /tmp/foo true
|
||||
echo "ok - can bind-mount a FUSE directory with $ALT"
|
||||
if [ "x$FUSE_DIR" != "x" ]; then
|
||||
$RUN $ALT --proc /proc --dev /dev --bind $FUSE_DIR /tmp/foo true
|
||||
ok "can bind-mount a FUSE directory with $ALT"
|
||||
else
|
||||
echo "ok # SKIP no FUSE support"
|
||||
ok_skip "no FUSE support"
|
||||
fi
|
||||
# no --dev => no devpts => no map_root workaround
|
||||
$RUN $ALT --proc /proc true
|
||||
echo "ok - can mount /proc with $ALT"
|
||||
ok "can mount /proc with $ALT"
|
||||
# No network
|
||||
$RUN $ALT --unshare-net --proc /proc --dev /dev true
|
||||
echo "ok - can unshare network, create new /dev with $ALT"
|
||||
ok "can unshare network, create new /dev with $ALT"
|
||||
# Unreadable file
|
||||
echo -n "expect EPERM: " >&2
|
||||
|
||||
|
@ -111,34 +49,56 @@ for ALT in "" "--unshare-user-try" "--unshare-pid" "--unshare-user-try --unshar
|
|||
CAP=""
|
||||
fi
|
||||
|
||||
if ! ${is_uidzero} && $RUN $CAP $ALT --unshare-net --proc /proc --bind /etc/shadow /tmp/foo cat /etc/shadow; then
|
||||
if ! cat /etc/shadow >/dev/null &&
|
||||
$RUN $CAP $ALT --unshare-net --proc /proc --bind /etc/shadow /tmp/foo cat /tmp/foo; then
|
||||
assert_not_reached Could read /etc/shadow via /tmp/foo bind-mount
|
||||
fi
|
||||
|
||||
if ! cat /etc/shadow >/dev/null &&
|
||||
$RUN $CAP $ALT --unshare-net --proc /proc --bind /etc/shadow /tmp/foo cat /etc/shadow; then
|
||||
assert_not_reached Could read /etc/shadow
|
||||
fi
|
||||
echo "ok - cannot read /etc/shadow with $ALT"
|
||||
|
||||
ok "cannot read /etc/shadow with $ALT"
|
||||
# Unreadable dir
|
||||
if [ x$UNREADABLE != x ]; then
|
||||
if [ "x$UNREADABLE" != "x" ]; then
|
||||
echo -n "expect EPERM: " >&2
|
||||
if $RUN $ALT --unshare-net --proc /proc --dev /dev --bind $UNREADABLE /tmp/foo cat /tmp/foo ; then
|
||||
if $RUN $ALT --unshare-net --proc /proc --dev /dev --bind $UNREADABLE /tmp/foo cat /tmp/foo; then
|
||||
assert_not_reached Could read $UNREADABLE
|
||||
fi
|
||||
echo "ok - cannot read $UNREADABLE with $ALT"
|
||||
ok "cannot read $UNREADABLE with $ALT"
|
||||
else
|
||||
echo "ok # SKIP not sure what unreadable file to use"
|
||||
ok_skip "not sure what unreadable file to use"
|
||||
fi
|
||||
|
||||
# bind dest in symlink (https://github.com/projectatomic/bubblewrap/pull/119)
|
||||
$RUN $ALT --dir /tmp/dir --symlink dir /tmp/link --bind /etc /tmp/link true
|
||||
echo "ok - can bind a destination over a symlink"
|
||||
ok "can bind a destination over a symlink"
|
||||
done
|
||||
|
||||
# Test symlink behaviour
|
||||
rm -f ./symlink
|
||||
$RUN --ro-bind / / --bind "$(pwd)" "$(pwd)" --symlink /dev/null "$(pwd)/symlink" true >&2
|
||||
readlink ./symlink > target.txt
|
||||
assert_file_has_content target.txt /dev/null
|
||||
ok "--symlink works"
|
||||
$RUN --ro-bind / / --bind "$(pwd)" "$(pwd)" --symlink /dev/null "$(pwd)/symlink" true >&2
|
||||
ok "--symlink is idempotent"
|
||||
if $RUN --ro-bind / / --bind "$(pwd)" "$(pwd)" --symlink /dev/full "$(pwd)/symlink" true 2>err.txt; then
|
||||
fatal "creating a conflicting symlink should have failed"
|
||||
else
|
||||
assert_file_has_content err.txt "Can't make symlink .*: existing destination is /dev/null"
|
||||
fi
|
||||
ok "--symlink doesn't overwrite a conflicting symlink"
|
||||
|
||||
# Test devices
|
||||
$RUN --unshare-pid --dev /dev ls -al /dev/{stdin,stdout,stderr,null,random,urandom,fd,core} >/dev/null
|
||||
echo "ok - all expected devices were created"
|
||||
ok "all expected devices were created"
|
||||
|
||||
# Test --as-pid-1
|
||||
$RUN --unshare-pid --as-pid-1 --bind / / bash -c 'echo $$' > as_pid_1.txt
|
||||
assert_file_has_content as_pid_1.txt "1"
|
||||
echo "ok - can run as pid 1"
|
||||
ok "can run as pid 1"
|
||||
|
||||
# Test --info-fd and --json-status-fd
|
||||
if $RUN --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'exit 42' 42>info.json 43>json-status.json 2>err.txt; then
|
||||
|
@ -147,7 +107,7 @@ fi
|
|||
assert_file_has_content info.json '"child-pid": [0-9]'
|
||||
assert_file_has_content json-status.json '"child-pid": [0-9]'
|
||||
assert_file_has_content_literal json-status.json '"exit-code": 42'
|
||||
echo "ok info and json-status fd"
|
||||
ok "info and json-status fd"
|
||||
|
||||
DATA=$($RUN --proc /proc --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'stat -L --format "%n %i" /proc/self/ns/*' 42>info.json 43>json-status.json 2>err.txt)
|
||||
|
||||
|
@ -158,58 +118,80 @@ for NS in "ipc" "mnt" "net" "pid" "uts"; do
|
|||
assert_file_has_content json-status.json "$want"
|
||||
done
|
||||
|
||||
echo "ok namespace id info in info and json-status fd"
|
||||
ok "namespace id info in info and json-status fd"
|
||||
|
||||
if ! which strace 2>/dev/null || ! strace -h | grep -v -e default | grep -e fault; then
|
||||
echo "ok - # SKIP no strace fault injection"
|
||||
if ! command -v strace >/dev/null || ! strace -h | grep -v -e default | grep -e fault >/dev/null; then
|
||||
ok_skip "no strace fault injection"
|
||||
else
|
||||
! strace -o /dev/null -f -e trace=prctl -e fault=prctl:when=39 $RUN --die-with-parent --json-status-fd 42 true 42>json-status.json
|
||||
assert_not_file_has_content json-status.json '"exit-code": [0-9]'
|
||||
echo "ok pre-exec failure doesn't include exit-code in json-status"
|
||||
ok "pre-exec failure doesn't include exit-code in json-status"
|
||||
fi
|
||||
|
||||
notanexecutable=/
|
||||
$RUN --json-status-fd 42 $notanexecutable 42>json-status.json || true
|
||||
assert_not_file_has_content json-status.json '"exit-code": [0-9]'
|
||||
echo "ok exec failure doesn't include exit-code in json-status"
|
||||
ok "exec failure doesn't include exit-code in json-status"
|
||||
|
||||
# These tests require --unshare-user
|
||||
if test -n "${bwrap_is_suid:-}"; then
|
||||
echo "ok - # SKIP no --cap-add support"
|
||||
echo "ok - # SKIP no --cap-add support"
|
||||
ok_skip "no --cap-add support"
|
||||
ok_skip "no --cap-add support"
|
||||
ok_skip "no --disable-userns"
|
||||
else
|
||||
BWRAP_RECURSE="$BWRAP --unshare-all --uid 0 --gid 0 --cap-add ALL --bind / / --bind /proc /proc"
|
||||
$BWRAP_RECURSE -- $BWRAP --unshare-all --bind / / --bind /proc /proc echo hello > recursive_proc.txt
|
||||
assert_file_has_content recursive_proc.txt "hello"
|
||||
echo "ok - can mount /proc recursively"
|
||||
BWRAP_RECURSE="$BWRAP --unshare-user --uid 0 --gid 0 --cap-add ALL --bind / / --bind /proc /proc"
|
||||
|
||||
$BWRAP_RECURSE -- $BWRAP --unshare-all ${BWRAP_RO_HOST_ARGS} findmnt > recursive-newroot.txt
|
||||
# $BWRAP May be inaccessible due to the user namespace so use /proc/self/exe
|
||||
$BWRAP_RECURSE -- /proc/self/exe --unshare-all --bind / / --bind /proc /proc echo hello > recursive_proc.txt
|
||||
assert_file_has_content recursive_proc.txt "hello"
|
||||
ok "can mount /proc recursively"
|
||||
|
||||
$BWRAP_RECURSE -- /proc/self/exe --unshare-all ${BWRAP_RO_HOST_ARGS} findmnt > recursive-newroot.txt
|
||||
assert_file_has_content recursive-newroot.txt "/usr"
|
||||
echo "ok - can pivot to new rootfs recursively"
|
||||
ok "can pivot to new rootfs recursively"
|
||||
|
||||
$BWRAP --dev-bind / / -- true
|
||||
! $BWRAP --assert-userns-disabled --dev-bind / / -- true
|
||||
$BWRAP --unshare-user --disable-userns --dev-bind / / -- true
|
||||
! $BWRAP --unshare-user --disable-userns --dev-bind / / -- $BWRAP --dev-bind / / -- true
|
||||
$BWRAP --unshare-user --disable-userns --dev-bind / / -- sh -c "echo 2 > /proc/sys/user/max_user_namespaces || true; ! $BWRAP --unshare-user --dev-bind / / -- true"
|
||||
$BWRAP --unshare-user --disable-userns --dev-bind / / -- sh -c "echo 100 > /proc/sys/user/max_user_namespaces || true; ! $BWRAP --unshare-user --dev-bind / / -- true"
|
||||
$BWRAP --unshare-user --disable-userns --dev-bind / / -- sh -c "! $BWRAP --unshare-user --dev-bind / / --assert-userns-disabled -- true"
|
||||
|
||||
$BWRAP_RECURSE --dev-bind / / -- true
|
||||
! $BWRAP_RECURSE --assert-userns-disabled --dev-bind / / -- true
|
||||
$BWRAP_RECURSE --unshare-user --disable-userns --dev-bind / / -- true
|
||||
! $BWRAP_RECURSE --unshare-user --disable-userns --dev-bind / / -- /proc/self/exe --dev-bind / / -- true
|
||||
$BWRAP_RECURSE --unshare-user --disable-userns --dev-bind / / -- sh -c "echo 2 > /proc/sys/user/max_user_namespaces || true; ! $BWRAP --unshare-user --dev-bind / / -- true"
|
||||
$BWRAP_RECURSE --unshare-user --disable-userns --dev-bind / / -- sh -c "echo 100 > /proc/sys/user/max_user_namespaces || true; ! $BWRAP --unshare-user --dev-bind / / -- true"
|
||||
$BWRAP_RECURSE --unshare-user --disable-userns --dev-bind / / -- sh -c "! $BWRAP --unshare-user --dev-bind / / --assert-userns-disabled -- true"
|
||||
|
||||
ok "can disable nested userns"
|
||||
fi
|
||||
|
||||
# Test error prefixing
|
||||
if $RUN --unshare-pid --bind /source-enoent /dest true 2>err.txt; then
|
||||
if $RUN --unshare-pid --bind /source-enoent /dest true 2>err.txt; then
|
||||
assert_not_reached "bound nonexistent source"
|
||||
fi
|
||||
assert_file_has_content err.txt "^bwrap: Can't find source path.*source-enoent"
|
||||
echo "ok error prefxing"
|
||||
ok "error prefixing"
|
||||
|
||||
if ! ${is_uidzero}; then
|
||||
# When invoked as non-root, check that by default we have no caps left
|
||||
for OPT in "" "--unshare-user-try --as-pid-1" "--unshare-user-try" "--as-pid-1"; do
|
||||
e=0
|
||||
$RUN $OPT --unshare-pid getpcaps 1 2> caps.test || e=$?
|
||||
$RUN $OPT --unshare-pid getpcaps 1 >&2 2> caps.test || e=$?
|
||||
sed -e 's/^/# /' < caps.test >&2
|
||||
test "$e" = 0
|
||||
assert_not_file_has_content caps.test ': =.*cap'
|
||||
done
|
||||
echo "ok - we have no caps as uid != 0"
|
||||
ok "we have no caps as uid != 0"
|
||||
else
|
||||
capsh --print > caps.orig
|
||||
capsh --print | sed -e 's/no-new-privs=0/no-new-privs=1/' > caps.expected
|
||||
|
||||
for OPT in "" "--as-pid-1"; do
|
||||
$RUN $OPT --unshare-pid capsh --print >caps.test
|
||||
diff -u caps.orig caps.test
|
||||
diff -u caps.expected caps.test
|
||||
done
|
||||
# And test that we can drop all, as well as specific caps
|
||||
$RUN $OPT --cap-drop ALL --unshare-pid capsh --print >caps.test
|
||||
|
@ -217,17 +199,17 @@ else
|
|||
# Check for dropping kill/fowner (we assume all uid 0 callers have this)
|
||||
# But we should still have net_bind_service for example
|
||||
$RUN $OPT --cap-drop CAP_KILL --cap-drop CAP_FOWNER --unshare-pid capsh --print >caps.test
|
||||
# capsh's output format changed from v2.29 -> drops are now indicated with -eip
|
||||
if grep 'Current: =.*+eip$' caps.test; then
|
||||
# capsh's output format changed from v2.29 -> drops are now indicated with -eip
|
||||
if grep 'Current: =.*+eip$' caps.test; then
|
||||
assert_not_file_has_content caps.test '^Current: =.*cap_kill.*+eip$'
|
||||
assert_not_file_has_content caps.test '^Current: =.*cap_fowner.*+eip$'
|
||||
assert_file_has_content caps.test '^Current: =.*cap_net_bind_service.*+eip$'
|
||||
else
|
||||
else
|
||||
assert_file_has_content caps.test '^Current: =eip.*cap_kill.*-eip$'
|
||||
assert_file_has_content caps.test '^Current: =eip.*cap_fowner.*-eip$'
|
||||
assert_not_file_has_content caps.test '^Current: =.*cap_net_bind_service.*-eip$'
|
||||
fi
|
||||
echo "ok - we have the expected caps as uid 0"
|
||||
ok "we have the expected caps as uid 0"
|
||||
fi
|
||||
|
||||
# Test --die-with-parent
|
||||
|
@ -255,7 +237,7 @@ for die_with_parent_argv in "--die-with-parent" "--die-with-parent --unshare-pid
|
|||
# We have to loop here, because bwrap doesn't wait for the lock if
|
||||
# another process is holding it. If we're unlucky, lockf-n.py will
|
||||
# be holding it.
|
||||
/bin/bash -c "while true; do $RUN ${die_with_parent_argv} --lock-file $(pwd)/lock sleep 1h; done" &
|
||||
bash -c "while true; do $RUN ${die_with_parent_argv} --lock-file $(pwd)/lock sleep 1h; done" &
|
||||
childshellpid=$!
|
||||
|
||||
# Wait for lock to be taken (yes hacky)
|
||||
|
@ -274,12 +256,14 @@ for die_with_parent_argv in "--die-with-parent" "--die-with-parent --unshare-pid
|
|||
kill -9 ${childshellpid}
|
||||
# Lock file should be unlocked
|
||||
./lockf-n.py ./lock wait
|
||||
echo "ok die with parent ${die_with_parent_argv}"
|
||||
ok "die with parent ${die_with_parent_argv}"
|
||||
done
|
||||
|
||||
printf '%s--dir\0/tmp/hello/world\0' '' > test.args
|
||||
$RUN --args 3 test -d /tmp/hello/world 3<test.args
|
||||
echo "ok - we can parse arguments from a fd"
|
||||
printf '%s--dir\0/tmp/hello/world2\0' '' > test.args2
|
||||
printf '%s--dir\0/tmp/hello/world3\0' '' > test.args3
|
||||
$RUN --args 3 --args 4 --args 5 /bin/sh -c 'test -d /tmp/hello/world && test -d /tmp/hello/world2 && test -d /tmp/hello/world3' 3<test.args 4<test.args2 5<test.args3
|
||||
ok "we can parse arguments from a fd"
|
||||
|
||||
mkdir bin
|
||||
echo "#!/bin/sh" > bin/--inadvisable-executable-name--
|
||||
|
@ -287,35 +271,35 @@ echo "echo hello" >> bin/--inadvisable-executable-name--
|
|||
chmod +x bin/--inadvisable-executable-name--
|
||||
PATH="${srcd}:$PATH" $RUN -- sh -c "echo hello" > stdout
|
||||
assert_file_has_content stdout hello
|
||||
echo "ok - we can run with --"
|
||||
ok "we can run with --"
|
||||
PATH="$(pwd)/bin:$PATH" $RUN -- --inadvisable-executable-name-- > stdout
|
||||
assert_file_has_content stdout hello
|
||||
echo "ok - we can run an inadvisable executable name with --"
|
||||
ok "we can run an inadvisable executable name with --"
|
||||
if $RUN -- --dev-bind /dev /dev sh -c 'echo should not have run'; then
|
||||
assert_not_reached "'--dev-bind' should have been interpreted as a (silly) executable name"
|
||||
fi
|
||||
echo "ok - options like --dev-bind are defanged by --"
|
||||
ok "options like --dev-bind are defanged by --"
|
||||
|
||||
if command -v mktemp > /dev/null; then
|
||||
tempfile="$(mktemp /tmp/bwrap-test-XXXXXXXX)"
|
||||
echo "hello" > "$tempfile"
|
||||
$BWRAP --bind / / cat "$tempfile" > stdout
|
||||
assert_file_has_content stdout hello
|
||||
echo "ok - bind-mount of / exposes real /tmp"
|
||||
ok "bind-mount of / exposes real /tmp"
|
||||
$BWRAP --bind / / --bind /tmp /tmp cat "$tempfile" > stdout
|
||||
assert_file_has_content stdout hello
|
||||
echo "ok - bind-mount of /tmp exposes real /tmp"
|
||||
if [ -d /mnt ]; then
|
||||
ok "bind-mount of /tmp exposes real /tmp"
|
||||
if [ -d /mnt ] && [ ! -L /mnt ]; then
|
||||
$BWRAP --bind / / --bind /tmp /mnt cat "/mnt/${tempfile#/tmp/}" > stdout
|
||||
assert_file_has_content stdout hello
|
||||
echo "ok - bind-mount of /tmp onto /mnt exposes real /tmp"
|
||||
ok "bind-mount of /tmp onto /mnt exposes real /tmp"
|
||||
else
|
||||
echo "ok - # SKIP /mnt does not exist"
|
||||
ok_skip "/mnt does not exist or is a symlink"
|
||||
fi
|
||||
else
|
||||
echo "ok - # SKIP mktemp not found"
|
||||
echo "ok - # SKIP mktemp not found"
|
||||
echo "ok - # SKIP mktemp not found"
|
||||
ok_skip "mktemp not found"
|
||||
ok_skip "mktemp not found"
|
||||
ok_skip "mktemp not found"
|
||||
fi
|
||||
|
||||
if $RUN test -d /tmp/oldroot; then
|
||||
|
@ -334,7 +318,7 @@ fi
|
|||
if $BWRAP --bind / / --bind "$(pwd)" /tmp test -d /tmp/newroot; then
|
||||
assert_not_reached "/tmp/newroot should not be visible"
|
||||
fi
|
||||
echo "ok - we can mount another directory onto /tmp"
|
||||
ok "we can mount another directory onto /tmp"
|
||||
|
||||
echo "hello" > input.$$
|
||||
$RUN --bind "$(pwd)" /tmp/here cat /tmp/here/input.$$ > stdout
|
||||
|
@ -345,42 +329,240 @@ fi
|
|||
if $RUN --bind "$(pwd)" /tmp/here test -d /tmp/newroot; then
|
||||
assert_not_reached "/tmp/newroot should not be visible"
|
||||
fi
|
||||
echo "ok - we can mount another directory inside /tmp"
|
||||
ok "we can mount another directory inside /tmp"
|
||||
|
||||
# These tests need user namespaces
|
||||
if test -n "${bwrap_is_suid:-}"; then
|
||||
echo "ok - # SKIP no setuid support for --unshare-user"
|
||||
echo "ok - # SKIP no setuid support for --unshare-user"
|
||||
touch some-file
|
||||
mkdir -p some-dir
|
||||
rm -fr new-dir-mountpoint
|
||||
rm -fr new-file-mountpoint
|
||||
$RUN \
|
||||
--bind "$(pwd -P)/some-dir" "$(pwd -P)/new-dir-mountpoint" \
|
||||
--bind "$(pwd -P)/some-file" "$(pwd -P)/new-file-mountpoint" \
|
||||
true
|
||||
command stat -c '%a' new-dir-mountpoint > new-dir-permissions
|
||||
assert_file_has_content new-dir-permissions 755
|
||||
command stat -c '%a' new-file-mountpoint > new-file-permissions
|
||||
assert_file_has_content new-file-permissions 444
|
||||
ok "Files and directories created as mount points have expected permissions"
|
||||
|
||||
|
||||
if [ -S /dev/log ]; then
|
||||
$RUN --bind / / --bind "$(realpath /dev/log)" "$(realpath /dev/log)" true
|
||||
ok "Can bind-mount a socket (/dev/log) onto a socket"
|
||||
else
|
||||
mkfifo donepipe
|
||||
|
||||
$RUN --info-fd 42 --unshare-user sh -c 'readlink /proc/self/ns/user > sandbox-userns; cat < donepipe' 42>info.json &
|
||||
while ! test -f sandbox-userns; do sleep 1; done
|
||||
SANDBOX1PID=$(extract_child_pid info.json)
|
||||
|
||||
$RUN --userns 11 readlink /proc/self/ns/user > sandbox2-userns 11< /proc/$SANDBOX1PID/ns/user
|
||||
echo foo > donepipe
|
||||
|
||||
assert_files_equal sandbox-userns sandbox2-userns
|
||||
|
||||
rm donepipe info.json sandbox-userns
|
||||
|
||||
echo "ok - Test --userns"
|
||||
|
||||
mkfifo donepipe
|
||||
$RUN --info-fd 42 --unshare-user --unshare-pid sh -c 'readlink /proc/self/ns/pid > sandbox-pidns; cat < donepipe' 42>info.json &
|
||||
while ! test -f sandbox-pidns; do sleep 1; done
|
||||
SANDBOX1PID=$(extract_child_pid info.json)
|
||||
|
||||
$RUN --userns 11 --pidns 12 readlink /proc/self/ns/pid > sandbox2-pidns 11< /proc/$SANDBOX1PID/ns/user 12< /proc/$SANDBOX1PID/ns/pid
|
||||
echo foo > donepipe
|
||||
|
||||
assert_files_equal sandbox-pidns sandbox2-pidns
|
||||
|
||||
rm donepipe info.json sandbox-pidns
|
||||
|
||||
echo "ok - Test --pidns"
|
||||
ok_skip "- /dev/log is not a socket, cannot test bubblewrap#409"
|
||||
fi
|
||||
|
||||
mkdir -p dir-already-existed
|
||||
chmod 0710 dir-already-existed
|
||||
mkdir -p dir-already-existed2
|
||||
chmod 0754 dir-already-existed2
|
||||
rm -fr new-dir-default-perms
|
||||
rm -fr new-dir-set-perms
|
||||
$RUN \
|
||||
--perms 1741 --dir "$(pwd -P)/new-dir-set-perms" \
|
||||
--dir "$(pwd -P)/dir-already-existed" \
|
||||
--perms 0741 --dir "$(pwd -P)/dir-already-existed2" \
|
||||
--dir "$(pwd -P)/dir-chmod" \
|
||||
--chmod 1755 "$(pwd -P)/dir-chmod" \
|
||||
--dir "$(pwd -P)/new-dir-default-perms" \
|
||||
true
|
||||
command stat -c '%a' new-dir-default-perms > new-dir-permissions
|
||||
assert_file_has_content new-dir-permissions '^755$'
|
||||
command stat -c '%a' new-dir-set-perms > new-dir-permissions
|
||||
assert_file_has_content new-dir-permissions '^1741$'
|
||||
command stat -c '%a' dir-already-existed > dir-permissions
|
||||
assert_file_has_content dir-permissions '^710$'
|
||||
command stat -c '%a' dir-already-existed2 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^754$'
|
||||
command stat -c '%a' dir-chmod > dir-permissions
|
||||
assert_file_has_content dir-permissions '^1755$'
|
||||
ok "Directories created explicitly have expected permissions"
|
||||
|
||||
echo "ok - End of test"
|
||||
rm -fr parent
|
||||
rm -fr parent-of-1777
|
||||
rm -fr parent-of-0755
|
||||
rm -fr parent-of-0644
|
||||
rm -fr parent-of-0750
|
||||
rm -fr parent-of-0710
|
||||
rm -fr parent-of-0720
|
||||
rm -fr parent-of-0640
|
||||
rm -fr parent-of-0700
|
||||
rm -fr parent-of-0600
|
||||
rm -fr parent-of-0705
|
||||
rm -fr parent-of-0604
|
||||
rm -fr parent-of-0000
|
||||
$RUN \
|
||||
--dir "$(pwd -P)"/parent/dir \
|
||||
--perms 1777 --dir "$(pwd -P)"/parent-of-1777/dir \
|
||||
--perms 0755 --dir "$(pwd -P)"/parent-of-0755/dir \
|
||||
--perms 0644 --dir "$(pwd -P)"/parent-of-0644/dir \
|
||||
--perms 0750 --dir "$(pwd -P)"/parent-of-0750/dir \
|
||||
--perms 0710 --dir "$(pwd -P)"/parent-of-0710/dir \
|
||||
--perms 0720 --dir "$(pwd -P)"/parent-of-0720/dir \
|
||||
--perms 0640 --dir "$(pwd -P)"/parent-of-0640/dir \
|
||||
--perms 0700 --dir "$(pwd -P)"/parent-of-0700/dir \
|
||||
--perms 0600 --dir "$(pwd -P)"/parent-of-0600/dir \
|
||||
--perms 0705 --dir "$(pwd -P)"/parent-of-0705/dir \
|
||||
--perms 0604 --dir "$(pwd -P)"/parent-of-0604/dir \
|
||||
--perms 0000 --dir "$(pwd -P)"/parent-of-0000/dir \
|
||||
true
|
||||
command stat -c '%a' parent > dir-permissions
|
||||
assert_file_has_content dir-permissions '^755$'
|
||||
command stat -c '%a' parent-of-1777 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^755$'
|
||||
command stat -c '%a' parent-of-0755 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^755$'
|
||||
command stat -c '%a' parent-of-0644 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^755$'
|
||||
command stat -c '%a' parent-of-0750 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^750$'
|
||||
command stat -c '%a' parent-of-0710 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^750$'
|
||||
command stat -c '%a' parent-of-0720 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^750$'
|
||||
command stat -c '%a' parent-of-0640 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^750$'
|
||||
command stat -c '%a' parent-of-0700 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^700$'
|
||||
command stat -c '%a' parent-of-0600 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^700$'
|
||||
command stat -c '%a' parent-of-0705 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^705$'
|
||||
command stat -c '%a' parent-of-0604 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^705$'
|
||||
command stat -c '%a' parent-of-0000 > dir-permissions
|
||||
assert_file_has_content dir-permissions '^700$'
|
||||
chmod -R 0700 parent*
|
||||
rm -fr parent*
|
||||
ok "Directories created as parents have expected permissions"
|
||||
|
||||
$RUN \
|
||||
--perms 01777 --tmpfs "$(pwd -P)" \
|
||||
cat /proc/self/mountinfo >&2
|
||||
$RUN \
|
||||
--perms 01777 --tmpfs "$(pwd -P)" \
|
||||
stat -c '%a' "$(pwd -P)" > dir-permissions
|
||||
assert_file_has_content dir-permissions '^1777$'
|
||||
$RUN \
|
||||
--tmpfs "$(pwd -P)" \
|
||||
stat -c '%a' "$(pwd -P)" > dir-permissions
|
||||
assert_file_has_content dir-permissions '^755$'
|
||||
ok "tmpfs has expected permissions"
|
||||
|
||||
# 1048576 = 1 MiB
|
||||
if test -n "${bwrap_is_suid:-}"; then
|
||||
if $RUN --size 1048576 --tmpfs "$(pwd -P)" true; then
|
||||
assert_not_reached "Should not allow --size --tmpfs when setuid"
|
||||
fi
|
||||
ok "--size --tmpfs is not allowed when setuid"
|
||||
elif df --output=size --block-size=1K "$(pwd -P)" >/dev/null 2>/dev/null; then
|
||||
$RUN \
|
||||
--size 1048576 --tmpfs "$(pwd -P)" \
|
||||
df --output=size --block-size=1K "$(pwd -P)" > dir-size
|
||||
assert_file_has_content dir-size '^ *1024$'
|
||||
$RUN \
|
||||
--size 1048576 --perms 01777 --tmpfs "$(pwd -P)" \
|
||||
stat -c '%a' "$(pwd -P)" > dir-permissions
|
||||
assert_file_has_content dir-permissions '^1777$'
|
||||
$RUN \
|
||||
--size 1048576 --perms 01777 --tmpfs "$(pwd -P)" \
|
||||
df --output=size --block-size=1K "$(pwd -P)" > dir-size
|
||||
assert_file_has_content dir-size '^ *1024$'
|
||||
$RUN \
|
||||
--perms 01777 --size 1048576 --tmpfs "$(pwd -P)" \
|
||||
stat -c '%a' "$(pwd -P)" > dir-permissions
|
||||
assert_file_has_content dir-permissions '^1777$'
|
||||
$RUN \
|
||||
--perms 01777 --size 1048576 --tmpfs "$(pwd -P)" \
|
||||
df --output=size --block-size=1K "$(pwd -P)" > dir-size
|
||||
assert_file_has_content dir-size '^ *1024$'
|
||||
ok "tmpfs has expected size"
|
||||
else
|
||||
$RUN --size 1048576 --tmpfs "$(pwd -P)" true
|
||||
$RUN --perms 01777 --size 1048576 --tmpfs "$(pwd -P)" true
|
||||
$RUN --size 1048576 --perms 01777 --tmpfs "$(pwd -P)" true
|
||||
ok_skip "df is too old, cannot test --size --tmpfs fully"
|
||||
fi
|
||||
|
||||
$RUN \
|
||||
--file 0 /tmp/file \
|
||||
stat -c '%a' /tmp/file < /dev/null > file-permissions
|
||||
assert_file_has_content file-permissions '^666$'
|
||||
$RUN \
|
||||
--perms 0640 --file 0 /tmp/file \
|
||||
stat -c '%a' /tmp/file < /dev/null > file-permissions
|
||||
assert_file_has_content file-permissions '^640$'
|
||||
$RUN \
|
||||
--bind-data 0 /tmp/file \
|
||||
stat -c '%a' /tmp/file < /dev/null > file-permissions
|
||||
assert_file_has_content file-permissions '^600$'
|
||||
$RUN \
|
||||
--perms 0640 --bind-data 0 /tmp/file \
|
||||
stat -c '%a' /tmp/file < /dev/null > file-permissions
|
||||
assert_file_has_content file-permissions '^640$'
|
||||
$RUN \
|
||||
--ro-bind-data 0 /tmp/file \
|
||||
stat -c '%a' /tmp/file < /dev/null > file-permissions
|
||||
assert_file_has_content file-permissions '^600$'
|
||||
$RUN \
|
||||
--perms 0640 --ro-bind-data 0 /tmp/file \
|
||||
stat -c '%a' /tmp/file < /dev/null > file-permissions
|
||||
assert_file_has_content file-permissions '^640$'
|
||||
ok "files have expected permissions"
|
||||
|
||||
if $RUN --size 0 --tmpfs /tmp/a true; then
|
||||
assert_not_reached Zero tmpfs size allowed
|
||||
fi
|
||||
if $RUN --size 123bogus --tmpfs /tmp/a true; then
|
||||
assert_not_reached Bogus tmpfs size allowed
|
||||
fi
|
||||
if $RUN --size '' --tmpfs /tmp/a true; then
|
||||
assert_not_reached Empty tmpfs size allowed
|
||||
fi
|
||||
if $RUN --size -12345678 --tmpfs /tmp/a true; then
|
||||
assert_not_reached Negative tmpfs size allowed
|
||||
fi
|
||||
if $RUN --size ' -12345678' --tmpfs /tmp/a true; then
|
||||
assert_not_reached Negative tmpfs size with space allowed
|
||||
fi
|
||||
# This is 2^64
|
||||
if $RUN --size 18446744073709551616 --tmpfs /tmp/a true; then
|
||||
assert_not_reached Overflowing tmpfs size allowed
|
||||
fi
|
||||
# This is 2^63 + 1; note that the current max size is SIZE_MAX/2
|
||||
if $RUN --size 9223372036854775809 --tmpfs /tmp/a true; then
|
||||
assert_not_reached Too-large tmpfs size allowed
|
||||
fi
|
||||
ok "bogus tmpfs size not allowed"
|
||||
|
||||
if $RUN --perms 0640 --perms 0640 --tmpfs /tmp/a true; then
|
||||
assert_not_reached Multiple perms options allowed
|
||||
fi
|
||||
if $RUN --size 1048576 --size 1048576 --tmpfs /tmp/a true; then
|
||||
assert_not_reached Multiple perms options allowed
|
||||
fi
|
||||
ok "--perms and --size only allowed once"
|
||||
|
||||
|
||||
FOO= BAR=baz $RUN --setenv FOO bar sh -c 'echo "$FOO$BAR"' > stdout
|
||||
assert_file_has_content stdout barbaz
|
||||
FOO=wrong BAR=baz $RUN --setenv FOO bar sh -c 'echo "$FOO$BAR"' > stdout
|
||||
assert_file_has_content stdout barbaz
|
||||
FOO=wrong BAR=baz $RUN --unsetenv FOO sh -c 'printf "%s%s" "$FOO" "$BAR"' > stdout
|
||||
printf baz > reference
|
||||
assert_files_equal stdout reference
|
||||
FOO=wrong BAR=wrong $RUN --clearenv /usr/bin/env > stdout
|
||||
echo "PWD=$(pwd -P)" > reference
|
||||
assert_files_equal stdout reference
|
||||
ok "environment manipulation"
|
||||
|
||||
$RUN sh -c 'echo $0' > stdout
|
||||
assert_file_has_content stdout sh
|
||||
$RUN --argv0 sh sh -c 'echo $0' > stdout
|
||||
assert_file_has_content stdout sh
|
||||
$RUN --argv0 right sh -c 'echo $0' > stdout
|
||||
assert_file_has_content stdout right
|
||||
ok "argv0 manipulation"
|
||||
|
||||
done_testing
|
||||
|
|
|
@ -0,0 +1,635 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright 2021 Simon McVittie
|
||||
# SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import termios
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import seccomp
|
||||
except ImportError:
|
||||
print('1..0 # SKIP cannot import seccomp Python module')
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
# This is the @default set from systemd as of 2021-10-11
|
||||
DEFAULT_SET = set('''
|
||||
brk
|
||||
cacheflush
|
||||
clock_getres
|
||||
clock_getres_time64
|
||||
clock_gettime
|
||||
clock_gettime64
|
||||
clock_nanosleep
|
||||
clock_nanosleep_time64
|
||||
execve
|
||||
exit
|
||||
exit_group
|
||||
futex
|
||||
futex_time64
|
||||
get_robust_list
|
||||
get_thread_area
|
||||
getegid
|
||||
getegid32
|
||||
geteuid
|
||||
geteuid32
|
||||
getgid
|
||||
getgid32
|
||||
getgroups
|
||||
getgroups32
|
||||
getpgid
|
||||
getpgrp
|
||||
getpid
|
||||
getppid
|
||||
getrandom
|
||||
getresgid
|
||||
getresgid32
|
||||
getresuid
|
||||
getresuid32
|
||||
getrlimit
|
||||
getsid
|
||||
gettid
|
||||
gettimeofday
|
||||
getuid
|
||||
getuid32
|
||||
membarrier
|
||||
mmap
|
||||
mmap2
|
||||
munmap
|
||||
nanosleep
|
||||
pause
|
||||
prlimit64
|
||||
restart_syscall
|
||||
rseq
|
||||
rt_sigreturn
|
||||
sched_getaffinity
|
||||
sched_yield
|
||||
set_robust_list
|
||||
set_thread_area
|
||||
set_tid_address
|
||||
set_tls
|
||||
sigreturn
|
||||
time
|
||||
ugetrlimit
|
||||
'''.split())
|
||||
|
||||
# This is the @basic-io set from systemd
|
||||
BASIC_IO_SET = set('''
|
||||
_llseek
|
||||
close
|
||||
close_range
|
||||
dup
|
||||
dup2
|
||||
dup3
|
||||
lseek
|
||||
pread64
|
||||
preadv
|
||||
preadv2
|
||||
pwrite64
|
||||
pwritev
|
||||
pwritev2
|
||||
read
|
||||
readv
|
||||
write
|
||||
writev
|
||||
'''.split())
|
||||
|
||||
# This is the @filesystem-io set from systemd
|
||||
FILESYSTEM_SET = set('''
|
||||
access
|
||||
chdir
|
||||
chmod
|
||||
close
|
||||
creat
|
||||
faccessat
|
||||
faccessat2
|
||||
fallocate
|
||||
fchdir
|
||||
fchmod
|
||||
fchmodat
|
||||
fcntl
|
||||
fcntl64
|
||||
fgetxattr
|
||||
flistxattr
|
||||
fremovexattr
|
||||
fsetxattr
|
||||
fstat
|
||||
fstat64
|
||||
fstatat64
|
||||
fstatfs
|
||||
fstatfs64
|
||||
ftruncate
|
||||
ftruncate64
|
||||
futimesat
|
||||
getcwd
|
||||
getdents
|
||||
getdents64
|
||||
getxattr
|
||||
inotify_add_watch
|
||||
inotify_init
|
||||
inotify_init1
|
||||
inotify_rm_watch
|
||||
lgetxattr
|
||||
link
|
||||
linkat
|
||||
listxattr
|
||||
llistxattr
|
||||
lremovexattr
|
||||
lsetxattr
|
||||
lstat
|
||||
lstat64
|
||||
mkdir
|
||||
mkdirat
|
||||
mknod
|
||||
mknodat
|
||||
newfstatat
|
||||
oldfstat
|
||||
oldlstat
|
||||
oldstat
|
||||
open
|
||||
openat
|
||||
openat2
|
||||
readlink
|
||||
readlinkat
|
||||
removexattr
|
||||
rename
|
||||
renameat
|
||||
renameat2
|
||||
rmdir
|
||||
setxattr
|
||||
stat
|
||||
stat64
|
||||
statfs
|
||||
statfs64
|
||||
statx
|
||||
symlink
|
||||
symlinkat
|
||||
truncate
|
||||
truncate64
|
||||
unlink
|
||||
unlinkat
|
||||
utime
|
||||
utimensat
|
||||
utimensat_time64
|
||||
utimes
|
||||
'''.split())
|
||||
|
||||
# Miscellaneous syscalls used during process startup, at least on x86_64
|
||||
ALLOWED = DEFAULT_SET | BASIC_IO_SET | FILESYSTEM_SET | set('''
|
||||
arch_prctl
|
||||
ioctl
|
||||
madvise
|
||||
mprotect
|
||||
mremap
|
||||
prctl
|
||||
readdir
|
||||
umask
|
||||
'''.split())
|
||||
|
||||
# Syscalls we will try to use, expecting them to be either allowed or
|
||||
# blocked by our allow and/or deny lists
|
||||
TRY_SYSCALLS = [
|
||||
'chmod',
|
||||
'chroot',
|
||||
'clone3',
|
||||
'ioctl TIOCNOTTY',
|
||||
'ioctl TIOCSTI CVE-2019-10063',
|
||||
'ioctl TIOCSTI',
|
||||
'listen',
|
||||
'prctl',
|
||||
]
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
if 'G_TEST_SRCDIR' in os.environ:
|
||||
self.test_srcdir = os.getenv('G_TEST_SRCDIR') + '/tests'
|
||||
else:
|
||||
self.test_srcdir = here
|
||||
|
||||
if 'G_TEST_BUILDDIR' in os.environ:
|
||||
self.test_builddir = os.getenv('G_TEST_BUILDDIR') + '/tests'
|
||||
else:
|
||||
self.test_builddir = here
|
||||
|
||||
self.bwrap = os.getenv('BWRAP', 'bwrap')
|
||||
self.try_syscall = os.path.join(self.test_builddir, 'try-syscall')
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'true',
|
||||
],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=2,
|
||||
)
|
||||
|
||||
if completed.returncode != 0:
|
||||
raise unittest.SkipTest(
|
||||
'cannot run bwrap (does it need to be setuid?)'
|
||||
)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
pass
|
||||
|
||||
def test_no_seccomp(self) -> None:
|
||||
for syscall in TRY_SYSCALLS:
|
||||
print('# {} without seccomp'.format(syscall))
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
self.try_syscall, syscall,
|
||||
],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=2,
|
||||
)
|
||||
|
||||
if (
|
||||
syscall == 'ioctl TIOCSTI CVE-2019-10063'
|
||||
and completed.returncode == errno.ENOENT
|
||||
):
|
||||
print('# Cannot test 64-bit syscall parameter on 32-bit')
|
||||
continue
|
||||
|
||||
if syscall == 'clone3':
|
||||
# If the kernel supports it, we didn't block it so
|
||||
# it fails with EFAULT. If the kernel doesn't support it,
|
||||
# it'll fail with ENOSYS instead.
|
||||
self.assertIn(
|
||||
completed.returncode,
|
||||
(errno.ENOSYS, errno.EFAULT),
|
||||
)
|
||||
elif syscall.startswith('ioctl') or syscall == 'listen':
|
||||
self.assertEqual(completed.returncode, errno.EBADF)
|
||||
else:
|
||||
self.assertEqual(completed.returncode, errno.EFAULT)
|
||||
|
||||
def test_seccomp_allowlist(self) -> None:
|
||||
with tempfile.TemporaryFile() as allowlist_temp:
|
||||
allowlist = seccomp.SyscallFilter(seccomp.ERRNO(errno.ENOSYS))
|
||||
|
||||
if os.uname().machine == 'x86_64':
|
||||
# Allow Python and try-syscall to be different word sizes
|
||||
allowlist.add_arch(seccomp.Arch.X86)
|
||||
|
||||
for syscall in ALLOWED:
|
||||
try:
|
||||
allowlist.add_rule(seccomp.ALLOW, syscall)
|
||||
except Exception as e:
|
||||
print('# Cannot add {} to allowlist: {!r}'.format(syscall, e))
|
||||
|
||||
allowlist.export_bpf(allowlist_temp)
|
||||
|
||||
for syscall in TRY_SYSCALLS:
|
||||
print('# allowlist vs. {}'.format(syscall))
|
||||
allowlist_temp.seek(0, os.SEEK_SET)
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--seccomp', str(allowlist_temp.fileno()),
|
||||
self.try_syscall, syscall,
|
||||
],
|
||||
pass_fds=(allowlist_temp.fileno(),),
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=2,
|
||||
)
|
||||
|
||||
if (
|
||||
syscall == 'ioctl TIOCSTI CVE-2019-10063'
|
||||
and completed.returncode == errno.ENOENT
|
||||
):
|
||||
print('# Cannot test 64-bit syscall parameter on 32-bit')
|
||||
continue
|
||||
|
||||
if syscall.startswith('ioctl'):
|
||||
# We allow this, so it is executed (and in this simple
|
||||
# example, immediately fails)
|
||||
self.assertEqual(completed.returncode, errno.EBADF)
|
||||
elif syscall in ('chroot', 'listen', 'clone3'):
|
||||
# We don't allow these, so they fail with ENOSYS.
|
||||
# clone3 might also be failing with ENOSYS because
|
||||
# the kernel genuinely doesn't support it.
|
||||
self.assertEqual(completed.returncode, errno.ENOSYS)
|
||||
else:
|
||||
# We allow this, so it is executed (and in this simple
|
||||
# example, immediately fails)
|
||||
self.assertEqual(completed.returncode, errno.EFAULT)
|
||||
|
||||
def test_seccomp_denylist(self) -> None:
|
||||
with tempfile.TemporaryFile() as denylist_temp:
|
||||
denylist = seccomp.SyscallFilter(seccomp.ALLOW)
|
||||
|
||||
if os.uname().machine == 'x86_64':
|
||||
# Allow Python and try-syscall to be different word sizes
|
||||
denylist.add_arch(seccomp.Arch.X86)
|
||||
|
||||
# Using ECONNREFUSED here because it's unlikely that any of
|
||||
# these syscalls will legitimately fail with that code, so
|
||||
# if they fail like this, it will be as a result of seccomp.
|
||||
denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chmod')
|
||||
denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chroot')
|
||||
denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'prctl')
|
||||
denylist.add_rule(
|
||||
seccomp.ERRNO(errno.ECONNREFUSED), 'ioctl',
|
||||
seccomp.Arg(1, seccomp.MASKED_EQ, 0xffffffff, termios.TIOCSTI),
|
||||
)
|
||||
|
||||
denylist.export_bpf(denylist_temp)
|
||||
|
||||
for syscall in TRY_SYSCALLS:
|
||||
print('# denylist vs. {}'.format(syscall))
|
||||
denylist_temp.seek(0, os.SEEK_SET)
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--seccomp', str(denylist_temp.fileno()),
|
||||
self.try_syscall, syscall,
|
||||
],
|
||||
pass_fds=(denylist_temp.fileno(),),
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=2,
|
||||
)
|
||||
|
||||
if (
|
||||
syscall == 'ioctl TIOCSTI CVE-2019-10063'
|
||||
and completed.returncode == errno.ENOENT
|
||||
):
|
||||
print('# Cannot test 64-bit syscall parameter on 32-bit')
|
||||
continue
|
||||
|
||||
if syscall == 'clone3':
|
||||
# If the kernel supports it, we didn't block it so
|
||||
# it fails with EFAULT. If the kernel doesn't support it,
|
||||
# it'll fail with ENOSYS instead.
|
||||
self.assertIn(
|
||||
completed.returncode,
|
||||
(errno.ENOSYS, errno.EFAULT),
|
||||
)
|
||||
elif syscall in ('ioctl TIOCNOTTY', 'listen'):
|
||||
# Not on the denylist
|
||||
self.assertEqual(completed.returncode, errno.EBADF)
|
||||
else:
|
||||
# We blocked all of these
|
||||
self.assertEqual(completed.returncode, errno.ECONNREFUSED)
|
||||
|
||||
def test_seccomp_stacked(self, allowlist_first=False) -> None:
|
||||
with tempfile.TemporaryFile(
|
||||
) as allowlist_temp, tempfile.TemporaryFile(
|
||||
) as denylist_temp:
|
||||
# This filter is a simplified version of what Flatpak wants
|
||||
|
||||
allowlist = seccomp.SyscallFilter(seccomp.ERRNO(errno.ENOSYS))
|
||||
denylist = seccomp.SyscallFilter(seccomp.ALLOW)
|
||||
|
||||
if os.uname().machine == 'x86_64':
|
||||
# Allow Python and try-syscall to be different word sizes
|
||||
allowlist.add_arch(seccomp.Arch.X86)
|
||||
denylist.add_arch(seccomp.Arch.X86)
|
||||
|
||||
for syscall in ALLOWED:
|
||||
try:
|
||||
allowlist.add_rule(seccomp.ALLOW, syscall)
|
||||
except Exception as e:
|
||||
print('# Cannot add {} to allowlist: {!r}'.format(syscall, e))
|
||||
|
||||
denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chmod')
|
||||
denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chroot')
|
||||
denylist.add_rule(
|
||||
seccomp.ERRNO(errno.ECONNREFUSED), 'ioctl',
|
||||
seccomp.Arg(1, seccomp.MASKED_EQ, 0xffffffff, termios.TIOCSTI),
|
||||
)
|
||||
|
||||
# All seccomp programs except the last must allow prctl(),
|
||||
# because otherwise we wouldn't be able to add the remaining
|
||||
# seccomp programs. We document that the last program can
|
||||
# block prctl, so test that.
|
||||
if allowlist_first:
|
||||
denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'prctl')
|
||||
|
||||
allowlist.export_bpf(allowlist_temp)
|
||||
denylist.export_bpf(denylist_temp)
|
||||
|
||||
for syscall in TRY_SYSCALLS:
|
||||
print('# stacked vs. {}'.format(syscall))
|
||||
allowlist_temp.seek(0, os.SEEK_SET)
|
||||
denylist_temp.seek(0, os.SEEK_SET)
|
||||
|
||||
if allowlist_first:
|
||||
fds = [allowlist_temp.fileno(), denylist_temp.fileno()]
|
||||
else:
|
||||
fds = [denylist_temp.fileno(), allowlist_temp.fileno()]
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--add-seccomp-fd', str(fds[0]),
|
||||
'--add-seccomp-fd', str(fds[1]),
|
||||
self.try_syscall, syscall,
|
||||
],
|
||||
pass_fds=fds,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=2,
|
||||
)
|
||||
|
||||
if (
|
||||
syscall == 'ioctl TIOCSTI CVE-2019-10063'
|
||||
and completed.returncode == errno.ENOENT
|
||||
):
|
||||
print('# Cannot test 64-bit syscall parameter on 32-bit')
|
||||
continue
|
||||
|
||||
if syscall == 'ioctl TIOCNOTTY':
|
||||
# Not denied by the denylist, and allowed by the allowlist
|
||||
self.assertEqual(completed.returncode, errno.EBADF)
|
||||
elif syscall in ('clone3', 'listen'):
|
||||
# We didn't deny these, so the denylist has no effect
|
||||
# and we fall back to the allowlist, which doesn't
|
||||
# include them either.
|
||||
# clone3 might also be failing with ENOSYS because
|
||||
# the kernel genuinely doesn't support it.
|
||||
self.assertEqual(completed.returncode, errno.ENOSYS)
|
||||
elif syscall == 'chroot':
|
||||
# This is denied by the denylist *and* not allowed by
|
||||
# the allowlist. The result depends which one we added
|
||||
# first: the most-recently-added filter "wins".
|
||||
if allowlist_first:
|
||||
self.assertEqual(
|
||||
completed.returncode,
|
||||
errno.ECONNREFUSED,
|
||||
)
|
||||
else:
|
||||
self.assertEqual(completed.returncode, errno.ENOSYS)
|
||||
elif syscall == 'prctl':
|
||||
# We can only put this on the denylist if the denylist
|
||||
# is the last to be added.
|
||||
if allowlist_first:
|
||||
self.assertEqual(
|
||||
completed.returncode,
|
||||
errno.ECONNREFUSED,
|
||||
)
|
||||
else:
|
||||
self.assertEqual(completed.returncode, errno.EFAULT)
|
||||
else:
|
||||
# chmod is allowed by the allowlist but blocked by the
|
||||
# denylist. Denying takes precedence over allowing,
|
||||
# regardless of order.
|
||||
self.assertEqual(completed.returncode, errno.ECONNREFUSED)
|
||||
|
||||
def test_seccomp_stacked_allowlist_first(self) -> None:
|
||||
self.test_seccomp_stacked(allowlist_first=True)
|
||||
|
||||
def test_seccomp_invalid(self) -> None:
|
||||
with tempfile.TemporaryFile(
|
||||
) as allowlist_temp, tempfile.TemporaryFile(
|
||||
) as denylist_temp:
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--add-seccomp-fd', '-1',
|
||||
'true',
|
||||
],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.assertIn(b'bwrap: Invalid fd: -1\n', completed.stderr)
|
||||
self.assertEqual(completed.returncode, 1)
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--seccomp', '0a',
|
||||
'true',
|
||||
],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.assertIn(b'bwrap: Invalid fd: 0a\n', completed.stderr)
|
||||
self.assertEqual(completed.returncode, 1)
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--add-seccomp-fd', str(denylist_temp.fileno()),
|
||||
'--seccomp', str(allowlist_temp.fileno()),
|
||||
'true',
|
||||
],
|
||||
pass_fds=(allowlist_temp.fileno(), denylist_temp.fileno()),
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.assertIn(
|
||||
b'bwrap: --seccomp cannot be combined with --add-seccomp-fd\n',
|
||||
completed.stderr,
|
||||
)
|
||||
self.assertEqual(completed.returncode, 1)
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--seccomp', str(allowlist_temp.fileno()),
|
||||
'--add-seccomp-fd', str(denylist_temp.fileno()),
|
||||
'true',
|
||||
],
|
||||
pass_fds=(allowlist_temp.fileno(), denylist_temp.fileno()),
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.assertIn(
|
||||
b'--add-seccomp-fd cannot be combined with --seccomp',
|
||||
completed.stderr,
|
||||
)
|
||||
self.assertEqual(completed.returncode, 1)
|
||||
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--add-seccomp-fd', str(allowlist_temp.fileno()),
|
||||
'--add-seccomp-fd', str(allowlist_temp.fileno()),
|
||||
'true',
|
||||
],
|
||||
pass_fds=(allowlist_temp.fileno(), allowlist_temp.fileno()),
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.assertIn(
|
||||
b"bwrap: Can't read seccomp data: ",
|
||||
completed.stderr,
|
||||
)
|
||||
self.assertEqual(completed.returncode, 1)
|
||||
|
||||
allowlist_temp.write(b'\x01')
|
||||
allowlist_temp.seek(0, os.SEEK_SET)
|
||||
completed = subprocess.run(
|
||||
[
|
||||
self.bwrap,
|
||||
'--ro-bind', '/', '/',
|
||||
'--add-seccomp-fd', str(denylist_temp.fileno()),
|
||||
'--add-seccomp-fd', str(allowlist_temp.fileno()),
|
||||
'true',
|
||||
],
|
||||
pass_fds=(allowlist_temp.fileno(), denylist_temp.fileno()),
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.assertIn(
|
||||
b'bwrap: Invalid seccomp data, must be multiple of 8\n',
|
||||
completed.stderr,
|
||||
)
|
||||
self.assertEqual(completed.returncode, 1)
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
try:
|
||||
from tap.runner import TAPTestRunner
|
||||
except ImportError:
|
||||
TAPTestRunner = None # type: ignore
|
||||
|
||||
if TAPTestRunner is not None:
|
||||
runner = TAPTestRunner()
|
||||
runner.set_stream(True)
|
||||
unittest.main(testRunner=runner)
|
||||
else:
|
||||
print('# tap.runner not available, using simple TAP output')
|
||||
print('1..1')
|
||||
program = unittest.main(exit=False)
|
||||
if program.result.wasSuccessful():
|
||||
print('ok 1 - %r' % program.result)
|
||||
else:
|
||||
print('not ok 1 - %r' % program.result)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
srcd=$(cd $(dirname "$0") && pwd)
|
||||
. "${srcd}/libtest.sh"
|
||||
|
||||
echo "1..1"
|
||||
|
||||
# This test needs user namespaces
|
||||
if test -n "${bwrap_is_suid:-}"; then
|
||||
echo "ok - # SKIP no setuid support for --unshare-user"
|
||||
else
|
||||
mkfifo donepipe
|
||||
$RUN --info-fd 42 --unshare-user --unshare-pid sh -c 'readlink /proc/self/ns/pid > sandbox-pidns; cat < donepipe' >/dev/null 42>info.json &
|
||||
while ! test -f sandbox-pidns; do sleep 1; done
|
||||
SANDBOX1PID=$(extract_child_pid info.json)
|
||||
|
||||
ASAN_OPTIONS=detect_leaks=0 LSAN_OPTIONS=detect_leaks=0 \
|
||||
$RUN --userns 11 --pidns 12 readlink /proc/self/ns/pid > sandbox2-pidns 11< /proc/$SANDBOX1PID/ns/user 12< /proc/$SANDBOX1PID/ns/pid
|
||||
echo foo > donepipe
|
||||
|
||||
assert_files_equal sandbox-pidns sandbox2-pidns
|
||||
|
||||
rm donepipe info.json sandbox-pidns
|
||||
|
||||
echo "ok - Test --pidns"
|
||||
fi
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
srcd=$(cd $(dirname "$0") && pwd)
|
||||
. "${srcd}/libtest.sh"
|
||||
|
||||
echo "1..1"
|
||||
|
||||
# This test needs user namespaces
|
||||
if test -n "${bwrap_is_suid:-}"; then
|
||||
echo "ok - # SKIP no setuid support for --unshare-user"
|
||||
else
|
||||
mkfifo donepipe
|
||||
|
||||
$RUN --info-fd 42 --unshare-user sh -c 'readlink /proc/self/ns/user > sandbox-userns; cat < donepipe' >/dev/null 42>info.json &
|
||||
while ! test -f sandbox-userns; do sleep 1; done
|
||||
SANDBOX1PID=$(extract_child_pid info.json)
|
||||
|
||||
$RUN --userns 11 readlink /proc/self/ns/user > sandbox2-userns 11< /proc/$SANDBOX1PID/ns/user
|
||||
echo foo > donepipe
|
||||
|
||||
assert_files_equal sandbox-userns sandbox2-userns
|
||||
|
||||
rm donepipe info.json sandbox-userns
|
||||
|
||||
echo "ok - Test --userns"
|
||||
fi
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright © 2019-2021 Collabora Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
/* A small implementation of TAP */
|
||||
static unsigned int test_number = 0;
|
||||
|
||||
__attribute__((format(printf, 1, 2)))
|
||||
static void
|
||||
ok (const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
printf ("ok %u - ", ++test_number);
|
||||
va_start (ap, format);
|
||||
vprintf (format, ap);
|
||||
va_end (ap);
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
/* for simplicity we always die immediately on failure */
|
||||
#define not_ok(fmt, ...) die (fmt, ## __VA_ARGS__)
|
||||
|
||||
/* approximately GLib-compatible helper macros */
|
||||
#define g_test_message(fmt, ...) printf ("# " fmt "\n", ## __VA_ARGS__)
|
||||
#define g_assert_cmpstr(left_expr, op, right_expr) \
|
||||
do { \
|
||||
const char *left = (left_expr); \
|
||||
const char *right = (right_expr); \
|
||||
if (strcmp0 (left, right) op 0) \
|
||||
ok ("%s (\"%s\") %s %s (\"%s\")", #left_expr, left, #op, #right_expr, right); \
|
||||
else \
|
||||
not_ok ("expected %s (\"%s\") %s %s (\"%s\")", \
|
||||
#left_expr, left, #op, #right_expr, right); \
|
||||
} while (0)
|
||||
#define g_assert_cmpint(left_expr, op, right_expr) \
|
||||
do { \
|
||||
intmax_t left = (left_expr); \
|
||||
intmax_t right = (right_expr); \
|
||||
if (left op right) \
|
||||
ok ("%s (%ji) %s %s (%ji)", #left_expr, left, #op, #right_expr, right); \
|
||||
else \
|
||||
not_ok ("expected %s (%ji) %s %s (%ji)", \
|
||||
#left_expr, left, #op, #right_expr, right); \
|
||||
} while (0)
|
||||
#define g_assert_cmpuint(left_expr, op, right_expr) \
|
||||
do { \
|
||||
uintmax_t left = (left_expr); \
|
||||
uintmax_t right = (right_expr); \
|
||||
if (left op right) \
|
||||
ok ("%s (%ju) %s %s (%ju)", #left_expr, left, #op, #right_expr, right); \
|
||||
else \
|
||||
not_ok ("expected %s (%ju) %s %s (%ju)", \
|
||||
#left_expr, left, #op, #right_expr, right); \
|
||||
} while (0)
|
||||
#define g_assert_true(expr) \
|
||||
do { \
|
||||
if ((expr)) \
|
||||
ok ("%s", #expr); \
|
||||
else \
|
||||
not_ok ("expected %s to be true", #expr); \
|
||||
} while (0)
|
||||
#define g_assert_false(expr) \
|
||||
do { \
|
||||
if (!(expr)) \
|
||||
ok ("!(%s)", #expr); \
|
||||
else \
|
||||
not_ok ("expected %s to be false", #expr); \
|
||||
} while (0)
|
||||
#define g_assert_null(expr) \
|
||||
do { \
|
||||
if ((expr) == NULL) \
|
||||
ok ("%s was null", #expr); \
|
||||
else \
|
||||
not_ok ("expected %s to be null", #expr); \
|
||||
} while (0)
|
||||
#define g_assert_nonnull(expr) \
|
||||
do { \
|
||||
if ((expr) != NULL) \
|
||||
ok ("%s wasn't null", #expr); \
|
||||
else \
|
||||
not_ok ("expected %s to be non-null", #expr); \
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
strcmp0 (const char *left,
|
||||
const char *right)
|
||||
{
|
||||
if (left == right)
|
||||
return 0;
|
||||
|
||||
if (left == NULL)
|
||||
return -1;
|
||||
|
||||
if (right == NULL)
|
||||
return 1;
|
||||
|
||||
return strcmp (left, right);
|
||||
}
|
||||
|
||||
static void
|
||||
test_n_elements (void)
|
||||
{
|
||||
int three[] = { 1, 2, 3 };
|
||||
g_assert_cmpuint (N_ELEMENTS (three), ==, 3);
|
||||
}
|
||||
|
||||
static void
|
||||
test_strconcat (void)
|
||||
{
|
||||
const char *a = "aaa";
|
||||
const char *b = "bbb";
|
||||
char *ab = strconcat (a, b);
|
||||
g_assert_cmpstr (ab, ==, "aaabbb");
|
||||
free (ab);
|
||||
}
|
||||
|
||||
static void
|
||||
test_strconcat3 (void)
|
||||
{
|
||||
const char *a = "aaa";
|
||||
const char *b = "bbb";
|
||||
const char *c = "ccc";
|
||||
char *abc = strconcat3 (a, b, c);
|
||||
g_assert_cmpstr (abc, ==, "aaabbbccc");
|
||||
free (abc);
|
||||
}
|
||||
|
||||
static void
|
||||
test_has_prefix (void)
|
||||
{
|
||||
g_assert_true (has_prefix ("foo", "foo"));
|
||||
g_assert_true (has_prefix ("foobar", "foo"));
|
||||
g_assert_false (has_prefix ("foobar", "fool"));
|
||||
g_assert_false (has_prefix ("foo", "fool"));
|
||||
g_assert_true (has_prefix ("foo", ""));
|
||||
g_assert_true (has_prefix ("", ""));
|
||||
g_assert_false (has_prefix ("", "no"));
|
||||
g_assert_false (has_prefix ("yes", "no"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_has_path_prefix (void)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
const char *str;
|
||||
const char *prefix;
|
||||
bool expected;
|
||||
} tests[] =
|
||||
{
|
||||
{ "/run/host/usr", "/run/host", TRUE },
|
||||
{ "/run/host/usr", "/run/host/", TRUE },
|
||||
{ "/run/host", "/run/host", TRUE },
|
||||
{ "////run///host////usr", "//run//host", TRUE },
|
||||
{ "////run///host////usr", "//run//host////", TRUE },
|
||||
{ "/run/hostage", "/run/host", FALSE },
|
||||
/* Any number of leading slashes is ignored, even zero */
|
||||
{ "foo/bar", "/foo", TRUE },
|
||||
{ "/foo/bar", "foo", TRUE },
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < N_ELEMENTS (tests); i++)
|
||||
{
|
||||
const char *str = tests[i].str;
|
||||
const char *prefix = tests[i].prefix;
|
||||
bool expected = tests[i].expected;
|
||||
|
||||
if (expected)
|
||||
g_test_message ("%s should have path prefix %s", str, prefix);
|
||||
else
|
||||
g_test_message ("%s should not have path prefix %s", str, prefix);
|
||||
|
||||
if (expected)
|
||||
g_assert_true (has_path_prefix (str, prefix));
|
||||
else
|
||||
g_assert_false (has_path_prefix (str, prefix));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc UNUSED,
|
||||
char **argv UNUSED)
|
||||
{
|
||||
setvbuf (stdout, NULL, _IONBF, 0);
|
||||
test_n_elements ();
|
||||
test_strconcat ();
|
||||
test_strconcat3 ();
|
||||
test_has_prefix ();
|
||||
test_has_path_prefix ();
|
||||
printf ("1..%u\n", test_number);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright 2021 Simon McVittie
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* Try one or more system calls that might have been blocked by a
|
||||
* seccomp filter. Return the last value of errno seen.
|
||||
*
|
||||
* In general, we pass a bad fd or pointer to each syscall that will
|
||||
* accept one, so that it will fail with EBADF or EFAULT without side-effects.
|
||||
*
|
||||
* This helper is used for regression tests in both bubblewrap and flatpak.
|
||||
* Please keep both copies in sync.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_MIPS_SIM)
|
||||
# if _MIPS_SIM == _ABIO32
|
||||
# define MISSING_SYSCALL_BASE 4000
|
||||
# elif _MIPS_SIM == _ABI64
|
||||
# define MISSING_SYSCALL_BASE 5000
|
||||
# elif _MIPS_SIM == _ABIN32
|
||||
# define MISSING_SYSCALL_BASE 6000
|
||||
# else
|
||||
# error "Unknown MIPS ABI"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__ia64__)
|
||||
# define MISSING_SYSCALL_BASE 1024
|
||||
#endif
|
||||
|
||||
#if defined(__alpha__)
|
||||
# define MISSING_SYSCALL_BASE 110
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) && defined(__ILP32__)
|
||||
# define MISSING_SYSCALL_BASE 0x40000000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MISSING_SYSCALL_BASE:
|
||||
*
|
||||
* Number to add to the syscall numbers of recently-added syscalls
|
||||
* to get the appropriate syscall for the current ABI.
|
||||
*/
|
||||
#ifndef MISSING_SYSCALL_BASE
|
||||
# define MISSING_SYSCALL_BASE 0
|
||||
#endif
|
||||
|
||||
#ifndef __NR_clone3
|
||||
# define __NR_clone3 (MISSING_SYSCALL_BASE + 435)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The size of clone3's parameter (as of 2021)
|
||||
*/
|
||||
#define SIZEOF_STRUCT_CLONE_ARGS ((size_t) 88)
|
||||
|
||||
/*
|
||||
* An invalid pointer that will cause syscalls to fail with EFAULT
|
||||
*/
|
||||
#define WRONG_POINTER ((char *) 1)
|
||||
|
||||
#ifndef PR_GET_CHILD_SUBREAPER
|
||||
#define PR_GET_CHILD_SUBREAPER 37
|
||||
#endif
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int errsv = 0;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (strcmp (arg, "print-errno-values") == 0)
|
||||
{
|
||||
printf ("EBADF=%d\n", EBADF);
|
||||
printf ("EFAULT=%d\n", EFAULT);
|
||||
printf ("ENOENT=%d\n", ENOENT);
|
||||
printf ("ENOSYS=%d\n", ENOSYS);
|
||||
printf ("EPERM=%d\n", EPERM);
|
||||
}
|
||||
else if (strcmp (arg, "chmod") == 0)
|
||||
{
|
||||
/* If not blocked by seccomp, this will fail with EFAULT */
|
||||
if (chmod (WRONG_POINTER, 0700) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
else if (strcmp (arg, "chroot") == 0)
|
||||
{
|
||||
/* If not blocked by seccomp, this will fail with EFAULT */
|
||||
if (chroot (WRONG_POINTER) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
else if (strcmp (arg, "clone3") == 0)
|
||||
{
|
||||
/* If not blocked by seccomp, this will fail with EFAULT */
|
||||
if (syscall (__NR_clone3, WRONG_POINTER, SIZEOF_STRUCT_CLONE_ARGS) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
else if (strcmp (arg, "ioctl TIOCNOTTY") == 0)
|
||||
{
|
||||
/* If not blocked by seccomp, this will fail with EBADF */
|
||||
if (ioctl (-1, TIOCNOTTY) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
else if (strcmp (arg, "ioctl TIOCSTI") == 0)
|
||||
{
|
||||
/* If not blocked by seccomp, this will fail with EBADF */
|
||||
if (ioctl (-1, TIOCSTI, WRONG_POINTER) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
#ifdef __LP64__
|
||||
else if (strcmp (arg, "ioctl TIOCSTI CVE-2019-10063") == 0)
|
||||
{
|
||||
unsigned long not_TIOCSTI = (0x123UL << 32) | (unsigned long) TIOCSTI;
|
||||
|
||||
/* If not blocked by seccomp, this will fail with EBADF */
|
||||
if (syscall (__NR_ioctl, -1, not_TIOCSTI, WRONG_POINTER) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (strcmp (arg, "listen") == 0)
|
||||
{
|
||||
/* If not blocked by seccomp, this will fail with EBADF */
|
||||
if (listen (-1, 42) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
else if (strcmp (arg, "prctl") == 0)
|
||||
{
|
||||
/* If not blocked by seccomp, this will fail with EFAULT */
|
||||
if (prctl (PR_GET_CHILD_SUBREAPER, WRONG_POINTER, 0, 0, 0) != 0)
|
||||
{
|
||||
errsv = errno;
|
||||
perror (arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "Unsupported syscall \"%s\"\n", arg);
|
||||
errsv = ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return errsv;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
/_build/
|
||||
/subprojects/
|
|
@ -0,0 +1,3 @@
|
|||
This is a simple example of a project that uses bubblewrap as a
|
||||
subproject. The intention is that if this project can successfully build
|
||||
bubblewrap as a subproject, then so could Flatpak.
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/python3
|
||||
# Copyright 2022 Collabora Ltd.
|
||||
# SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
completed = subprocess.run(
|
||||
['objdump', '-T', '-x', sys.argv[1]],
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
stdout = completed.stdout
|
||||
assert stdout is not None
|
||||
seen_rpath = False
|
||||
|
||||
for line in stdout.splitlines():
|
||||
words = line.strip().split()
|
||||
|
||||
if words and words[0] in (b'RPATH', b'RUNPATH'):
|
||||
print(line.decode(errors='backslashreplace'))
|
||||
assert len(words) == 2, words
|
||||
assert words[1] == b'${ORIGIN}/../lib', words
|
||||
seen_rpath = True
|
||||
|
||||
assert seen_rpath
|
|
@ -0,0 +1 @@
|
|||
#error Should not use superproject config.h to compile bubblewrap
|
|
@ -0,0 +1 @@
|
|||
#error Should not use superproject generated config.h to compile bubblewrap
|
|
@ -0,0 +1,20 @@
|
|||
project(
|
||||
'use-bubblewrap-as-subproject',
|
||||
'c',
|
||||
version : '0',
|
||||
meson_version : '>=0.49.0',
|
||||
)
|
||||
|
||||
configure_file(
|
||||
output : 'config.h',
|
||||
input : 'dummy-config.h.in',
|
||||
configuration : configuration_data(),
|
||||
)
|
||||
|
||||
subproject(
|
||||
'bubblewrap',
|
||||
default_options : [
|
||||
'install_rpath=${ORIGIN}/../lib',
|
||||
'program_prefix=not-flatpak-',
|
||||
],
|
||||
)
|
181
utils.c
181
utils.c
|
@ -1,5 +1,6 @@
|
|||
/* bubblewrap
|
||||
* Copyright (C) 2016 Alexander Larsson
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -18,28 +19,72 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/socket.h>
|
||||
#ifdef HAVE_SELINUX
|
||||
#include <selinux/selinux.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SELINUX_2_3
|
||||
/* libselinux older than 2.3 weren't const-correct */
|
||||
#define setexeccon(x) setexeccon ((security_context_t) x)
|
||||
#define setfscreatecon(x) setfscreatecon ((security_context_t) x)
|
||||
#define security_check_context(x) security_check_context ((security_context_t) x)
|
||||
#endif
|
||||
|
||||
__attribute__((format(printf, 1, 0))) static void
|
||||
warnv (const char *format,
|
||||
va_list args,
|
||||
const char *detail)
|
||||
{
|
||||
fprintf (stderr, "bwrap: ");
|
||||
vfprintf (stderr, format, args);
|
||||
|
||||
if (detail != NULL)
|
||||
fprintf (stderr, ": %s", detail);
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
warn (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start (args, format);
|
||||
warnv (format, args, NULL);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
void
|
||||
die_with_error (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int errsv;
|
||||
|
||||
fprintf (stderr, "bwrap: ");
|
||||
errsv = errno;
|
||||
|
||||
va_start (args, format);
|
||||
warnv (format, args, strerror (errsv));
|
||||
va_end (args);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
void
|
||||
die_with_mount_error (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int errsv;
|
||||
|
||||
errsv = errno;
|
||||
|
||||
va_start (args, format);
|
||||
vfprintf (stderr, format, args);
|
||||
warnv (format, args, mount_strerror (errsv));
|
||||
va_end (args);
|
||||
|
||||
fprintf (stderr, ": %s\n", strerror (errsv));
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
@ -48,24 +93,20 @@ die (const char *format, ...)
|
|||
{
|
||||
va_list args;
|
||||
|
||||
fprintf (stderr, "bwrap: ");
|
||||
|
||||
va_start (args, format);
|
||||
vfprintf (stderr, format, args);
|
||||
warnv (format, args, NULL);
|
||||
va_end (args);
|
||||
|
||||
fprintf (stderr, "\n");
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
void
|
||||
die_unless_label_valid (const char *label)
|
||||
die_unless_label_valid (UNUSED const char *label)
|
||||
{
|
||||
#ifdef HAVE_SELINUX
|
||||
if (is_selinux_enabled () == 1)
|
||||
{
|
||||
if (security_check_context ((security_context_t) label) < 0)
|
||||
if (security_check_context (label) < 0)
|
||||
die_with_error ("invalid label %s", label);
|
||||
return;
|
||||
}
|
||||
|
@ -104,9 +145,9 @@ xmalloc (size_t size)
|
|||
}
|
||||
|
||||
void *
|
||||
xcalloc (size_t size)
|
||||
xcalloc (size_t nmemb, size_t size)
|
||||
{
|
||||
void *res = calloc (1, size);
|
||||
void *res = calloc (nmemb, size);
|
||||
|
||||
if (res == NULL)
|
||||
die_oom ();
|
||||
|
@ -116,9 +157,13 @@ xcalloc (size_t size)
|
|||
void *
|
||||
xrealloc (void *ptr, size_t size)
|
||||
{
|
||||
void *res = realloc (ptr, size);
|
||||
void *res;
|
||||
|
||||
if (size != 0 && res == NULL)
|
||||
assert (size != 0);
|
||||
|
||||
res = realloc (ptr, size);
|
||||
|
||||
if (res == NULL)
|
||||
die_oom ();
|
||||
return res;
|
||||
}
|
||||
|
@ -230,6 +275,13 @@ has_prefix (const char *str,
|
|||
return strncmp (str, prefix, strlen (prefix)) == 0;
|
||||
}
|
||||
|
||||
void
|
||||
xclearenv (void)
|
||||
{
|
||||
if (clearenv () != 0)
|
||||
die_with_error ("clearenv failed");
|
||||
}
|
||||
|
||||
void
|
||||
xsetenv (const char *name, const char *value, int overwrite)
|
||||
{
|
||||
|
@ -448,9 +500,14 @@ ensure_file (const char *path,
|
|||
|
||||
/* We check this ahead of time, otherwise
|
||||
the create file will fail in the read-only
|
||||
case with EROFS instead of EEXIST */
|
||||
case with EROFS instead of EEXIST.
|
||||
|
||||
We're trying to set up a mount point for a non-directory, so any
|
||||
non-directory, non-symlink is acceptable - it doesn't necessarily
|
||||
have to be a regular file. */
|
||||
if (stat (path, &buf) == 0 &&
|
||||
S_ISREG (buf.st_mode))
|
||||
!S_ISDIR (buf.st_mode) &&
|
||||
!S_ISLNK (buf.st_mode))
|
||||
return 0;
|
||||
|
||||
if (create_file (path, mode, NULL) != 0 && errno != EEXIST)
|
||||
|
@ -534,7 +591,6 @@ load_file_data (int fd,
|
|||
ssize_t data_read;
|
||||
ssize_t data_len;
|
||||
ssize_t res;
|
||||
int errsv;
|
||||
|
||||
data_read = 0;
|
||||
data_len = 4080;
|
||||
|
@ -544,6 +600,12 @@ load_file_data (int fd,
|
|||
{
|
||||
if (data_len == data_read + 1)
|
||||
{
|
||||
if (data_len > SSIZE_MAX / 2)
|
||||
{
|
||||
errno = EFBIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_len *= 2;
|
||||
data = xrealloc (data, data_len);
|
||||
}
|
||||
|
@ -553,12 +615,7 @@ load_file_data (int fd,
|
|||
while (res < 0 && errno == EINTR);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
errsv = errno;
|
||||
close (fd);
|
||||
errno = errsv;
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
data_read += res;
|
||||
}
|
||||
|
@ -638,7 +695,7 @@ ensure_dir (const char *path,
|
|||
/* Sets errno on error (!= 0) */
|
||||
int
|
||||
mkdir_with_parents (const char *pathname,
|
||||
int mode,
|
||||
mode_t mode,
|
||||
bool create_last)
|
||||
{
|
||||
cleanup_free char *fn = NULL;
|
||||
|
@ -745,7 +802,7 @@ read_pid_from_socket (int socket)
|
|||
msg.msg_controllen = control_len_rcv;
|
||||
|
||||
if (recvmsg (socket, &msg, 0) < 0)
|
||||
die_with_error ("Cant read pid from socket");
|
||||
die_with_error ("Can't read pid from socket");
|
||||
|
||||
if (msg.msg_controllen <= 0)
|
||||
die ("Unexpected short read from pid socket");
|
||||
|
@ -764,6 +821,47 @@ read_pid_from_socket (int socket)
|
|||
die ("No pid returned on socket");
|
||||
}
|
||||
|
||||
/* Sets errno on error (== NULL),
|
||||
* Always ensures terminating zero */
|
||||
char *
|
||||
readlink_malloc (const char *pathname)
|
||||
{
|
||||
size_t size = 50;
|
||||
ssize_t n;
|
||||
cleanup_free char *value = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
if (size > SIZE_MAX / 2)
|
||||
die ("Symbolic link target pathname too long");
|
||||
size *= 2;
|
||||
value = xrealloc (value, size);
|
||||
n = readlink (pathname, value, size - 1);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
}
|
||||
while (size - 2 < (size_t)n);
|
||||
|
||||
value[n] = 0;
|
||||
return steal_pointer (&value);
|
||||
}
|
||||
|
||||
char *
|
||||
get_oldroot_path (const char *path)
|
||||
{
|
||||
while (*path == '/')
|
||||
path++;
|
||||
return strconcat ("/oldroot/", path);
|
||||
}
|
||||
|
||||
char *
|
||||
get_newroot_path (const char *path)
|
||||
{
|
||||
while (*path == '/')
|
||||
path++;
|
||||
return strconcat ("/newroot/", path);
|
||||
}
|
||||
|
||||
int
|
||||
raw_clone (unsigned long flags,
|
||||
void *child_stack)
|
||||
|
@ -789,7 +887,7 @@ pivot_root (const char * new_root, const char * put_old)
|
|||
}
|
||||
|
||||
char *
|
||||
label_mount (const char *opt, const char *mount_label)
|
||||
label_mount (const char *opt, UNUSED const char *mount_label)
|
||||
{
|
||||
#ifdef HAVE_SELINUX
|
||||
if (mount_label)
|
||||
|
@ -806,21 +904,42 @@ label_mount (const char *opt, const char *mount_label)
|
|||
}
|
||||
|
||||
int
|
||||
label_create_file (const char *file_label)
|
||||
label_create_file (UNUSED const char *file_label)
|
||||
{
|
||||
#ifdef HAVE_SELINUX
|
||||
if (is_selinux_enabled () > 0 && file_label)
|
||||
return setfscreatecon ((security_context_t) file_label);
|
||||
return setfscreatecon (file_label);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
label_exec (const char *exec_label)
|
||||
label_exec (UNUSED const char *exec_label)
|
||||
{
|
||||
#ifdef HAVE_SELINUX
|
||||
if (is_selinux_enabled () > 0 && exec_label)
|
||||
return setexeccon ((security_context_t) exec_label);
|
||||
return setexeccon (exec_label);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Like strerror(), but specialized for a failed mount(2) call.
|
||||
*/
|
||||
const char *
|
||||
mount_strerror (int errsv)
|
||||
{
|
||||
switch (errsv)
|
||||
{
|
||||
case ENOSPC:
|
||||
/* "No space left on device" misleads users into thinking there
|
||||
* is some sort of disk-space problem, but mount(2) uses that
|
||||
* errno value to mean something more like "limit exceeded". */
|
||||
return ("Limit exceeded (ENOSPC). "
|
||||
"(Hint: Check that /proc/sys/fs/mount-max is sufficient, "
|
||||
"typically 100000)");
|
||||
|
||||
default:
|
||||
return strerror (errsv);
|
||||
}
|
||||
}
|
||||
|
|
19
utils.h
19
utils.h
|
@ -1,5 +1,6 @@
|
|||
/* bubblewrap
|
||||
* Copyright (C) 2016 Alexander Larsson
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -47,8 +48,16 @@ typedef int bool;
|
|||
#define PIPE_READ_END 0
|
||||
#define PIPE_WRITE_END 1
|
||||
|
||||
#ifndef PR_SET_CHILD_SUBREAPER
|
||||
#define PR_SET_CHILD_SUBREAPER 36
|
||||
#endif
|
||||
|
||||
void warn (const char *format,
|
||||
...) __attribute__((format (printf, 1, 2)));
|
||||
void die_with_error (const char *format,
|
||||
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
|
||||
void die_with_mount_error (const char *format,
|
||||
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
|
||||
void die (const char *format,
|
||||
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
|
||||
void die_oom (void) __attribute__((__noreturn__));
|
||||
|
@ -57,11 +66,12 @@ void die_unless_label_valid (const char *label);
|
|||
void fork_intermediate_child (void);
|
||||
|
||||
void *xmalloc (size_t size);
|
||||
void *xcalloc (size_t size);
|
||||
void *xcalloc (size_t nmemb, size_t size);
|
||||
void *xrealloc (void *ptr,
|
||||
size_t size);
|
||||
char *xstrdup (const char *str);
|
||||
void strfreev (char **str_array);
|
||||
void xclearenv (void);
|
||||
void xsetenv (const char *name,
|
||||
const char *value,
|
||||
int overwrite);
|
||||
|
@ -107,11 +117,14 @@ int ensure_dir (const char *path,
|
|||
mode_t mode);
|
||||
int get_file_mode (const char *pathname);
|
||||
int mkdir_with_parents (const char *pathname,
|
||||
int mode,
|
||||
mode_t mode,
|
||||
bool create_last);
|
||||
void create_pid_socketpair (int sockets[2]);
|
||||
void send_pid_on_socket (int socket);
|
||||
int read_pid_from_socket (int socket);
|
||||
char *get_oldroot_path (const char *path);
|
||||
char *get_newroot_path (const char *path);
|
||||
char *readlink_malloc (const char *pathname);
|
||||
|
||||
/* syscall wrappers */
|
||||
int raw_clone (unsigned long flags,
|
||||
|
@ -123,6 +136,8 @@ char *label_mount (const char *opt,
|
|||
int label_exec (const char *exec_label);
|
||||
int label_create_file (const char *file_label);
|
||||
|
||||
const char *mount_strerror (int errsv);
|
||||
|
||||
static inline void
|
||||
cleanup_freep (void *p)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue