Import Upstream version 0.9.0

This commit is contained in:
luoyaoming 2024-04-24 09:12:01 +08:00
parent c8f7fdb963
commit 015eefdf88
80 changed files with 4207 additions and 28162 deletions

View File

@ -3,4 +3,3 @@ indent_style = space
indent_size = 2 indent_size = 2
trim_trailing_whitespace = true trim_trailing_whitespace = true
indent_brace_style = gnu indent_brace_style = gnu

167
.github/workflows/check.yml vendored Normal file
View File

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

3
CODE-OF-CONDUCT.md Normal file
View File

@ -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
LICENSE Symbolic link
View File

@ -0,0 +1 @@
COPYING

View File

@ -1,4 +1,3 @@
bwrap_SOURCES = \ bwrap_SOURCES = \
$(bwrap_srcpath)/bubblewrap.c \ $(bwrap_srcpath)/bubblewrap.c \
$(bwrap_srcpath)/bind-mount.h \ $(bwrap_srcpath)/bind-mount.h \

View File

@ -5,11 +5,21 @@ EXTRA_DIST = \
.editorconfig \ .editorconfig \
README.md \ README.md \
autogen.sh \ autogen.sh \
completions/bash/meson.build \
completions/meson.build \
completions/zsh/meson.build \
demos/bubblewrap-shell.sh \ demos/bubblewrap-shell.sh \
demos/flatpak-run.sh \ demos/flatpak-run.sh \
demos/flatpak.bpf \ demos/flatpak.bpf \
demos/userns-block-fd.py \ demos/userns-block-fd.py \
meson.build \
meson_options.txt \
packaging/bubblewrap.spec \ 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.cfg \
uncrustify.sh \ uncrustify.sh \
$(NULL) $(NULL)
@ -27,33 +37,64 @@ if PRIV_MODE_SETUID
$(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap $(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap
endif 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 rm -rf test-bwrap
cp bwrap test-bwrap cp bwrap test-bwrap
chmod 0755 test-bwrap
if PRIV_MODE_SETUID if PRIV_MODE_SETUID
$(SUDO_BIN) chown root test-bwrap $(SUDO_BIN) chown root test-bwrap
$(SUDO_BIN) chmod u+s test-bwrap $(SUDO_BIN) chmod u+s test-bwrap
endif endif
tests_test_utils_SOURCES = \
tests/test-utils.c \
utils.h \
utils.c \
$(NULL)
tests_test_utils_LDADD = $(SELINUX_LIBS)
test_bwrap_SOURCES= test_bwrap_SOURCES=
include Makefile-docs.am include Makefile-docs.am
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
LOG_COMPILER = LOG_COMPILER =
TESTS = tests/test-run.sh TESTS_ENVIRONMENT = \
TESTS_ENVIRONMENT = BWRAP=$(abs_top_builddir)/test-bwrap 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-core.sh
EXTRA_DIST += tests/libtest.sh
if ENABLE_BASH_COMPLETION if ENABLE_BASH_COMPLETION
bashcompletiondir = $(BASH_COMPLETION_DIR) bashcompletiondir = $(BASH_COMPLETION_DIR)
dist_bashcompletion_DATA = completions/bash/bwrap dist_bashcompletion_DATA = completions/bash/bwrap
endif endif
if ENABLE_ZSH_COMPLETION
zshcompletiondir = $(ZSH_COMPLETION_DIR)
dist_zshcompletion_DATA = completions/zsh/_bwrap
endif
-include $(top_srcdir)/git.mk -include $(top_srcdir)/git.mk
AM_DISTCHECK_CONFIGURE_FLAGS = \ AM_DISTCHECK_CONFIGURE_FLAGS = \

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ etc. focus on providing infrastructure for system administrators and
orchestration tools (e.g. Kubernetes) to run containers. orchestration tools (e.g. Kubernetes) to run containers.
These tools are not suitable to give to unprivileged users, because it 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. on the host.
User namespaces User namespaces
@ -31,12 +31,12 @@ user namespaces. Emphasis on subset - specifically relevant to the
above CVE, bubblewrap does not allow control over iptables. above CVE, bubblewrap does not allow control over iptables.
The original bubblewrap code existed before user namespaces - it inherits code from 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 which in turn distantly derives from
[linux-user-chroot](https://git.gnome.org/browse/linux-user-chroot). [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 The maintainers of this tool believe that it does not, even when used
in combination with typical software installed on that distribution, 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 setuid binaries, which is the [traditional way](https://en.wikipedia.org/wiki/Chroot#Limitations) to get out of things
like chroots. 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 Users
----- -----
@ -62,6 +86,31 @@ clusters. Having the ability for unprivileged users to use container
features would make it significantly easier to do interactive features would make it significantly easier to do interactive
debugging scenarios and the like. 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 Usage
----- -----
@ -77,7 +126,14 @@ source code, but here's a trimmed down version which runs
a new shell reusing the host's `/usr`. 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 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). 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 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 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 a setuid tool with a lot of desktop-specific sandboxing features. For
example, Firejail knows about Pulseaudio, whereas bubblewrap does not. example, Firejail knows about Pulseaudio, whereas bubblewrap does not.

42
SECURITY.md Normal file
View File

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

1516
aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
#!/bin/sh #!/bin/sh
test -n "$srcdir" || srcdir=`dirname "$0"` test -n "$srcdir" || srcdir=$(dirname "$0")
test -n "$srcdir" || srcdir=. test -n "$srcdir" || srcdir=.
olddir=`pwd` olddir=$(pwd)
cd $srcdir cd "$srcdir"
if ! (autoreconf --version >/dev/null 2>&1); then if ! (autoreconf --version >/dev/null 2>&1); then
echo "*** No autoreconf found, please install it ***" echo "*** No autoreconf found, please install it ***"
@ -15,5 +15,5 @@ mkdir -p m4
autoreconf --force --install --verbose autoreconf --force --install --verbose
cd $olddir cd "$olddir"
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"

View File

@ -1,5 +1,6 @@
/* bubblewrap /* bubblewrap
* Copyright (C) 2016 Alexander Larsson * Copyright (C) 2016 Alexander Larsson
* SPDX-License-Identifier: LGPL-2.0-or-later
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -85,7 +86,7 @@ decode_mountoptions (const char *options)
int i; int i;
unsigned long flags = 0; unsigned long flags = 0;
static const struct { int flag; static const struct { int flag;
char *name; const char *name;
} flags_data[] = { } flags_data[] = {
{ 0, "rw" }, { 0, "rw" },
{ MS_RDONLY, "ro" }, { MS_RDONLY, "ro" },
@ -236,7 +237,7 @@ parse_mountinfo (int proc_fd,
MountInfo *end_tab; MountInfo *end_tab;
int n_mounts; int n_mounts;
char *line; char *line;
int i; unsigned int i;
int max_id; int max_id;
unsigned int n_lines; unsigned int n_lines;
int root; int root;
@ -246,7 +247,7 @@ parse_mountinfo (int proc_fd,
die_with_error ("Can't open /proc/self/mountinfo"); die_with_error ("Can't open /proc/self/mountinfo");
n_lines = count_lines (mountinfo); n_lines = count_lines (mountinfo);
lines = xcalloc (n_lines * sizeof (MountInfoLine)); lines = xcalloc (n_lines, sizeof (MountInfoLine));
max_id = 0; max_id = 0;
line = mountinfo; line = mountinfo;
@ -309,11 +310,11 @@ parse_mountinfo (int proc_fd,
if (root == -1) if (root == -1)
{ {
mount_tab = xcalloc (sizeof (MountInfo) * (1)); mount_tab = xcalloc (1, sizeof (MountInfo));
return steal_pointer (&mount_tab); 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++) for (i = 0; i < n_lines; i++)
by_id[lines[i].id] = &lines[i]; by_id[lines[i].id] = &lines[i];
@ -365,7 +366,7 @@ parse_mountinfo (int proc_fd,
} }
n_mounts = count_mounts (&lines[root]); 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]); end_tab = collect_mounts (&mount_tab[0], &lines[root]);
assert (end_tab == &mount_tab[n_mounts]); assert (end_tab == &mount_tab[n_mounts]);
@ -373,11 +374,12 @@ parse_mountinfo (int proc_fd,
return steal_pointer (&mount_tab); return steal_pointer (&mount_tab);
} }
int bind_mount_result
bind_mount (int proc_fd, bind_mount (int proc_fd,
const char *src, const char *src,
const char *dest, const char *dest,
bind_option_t options) bind_option_t options,
char **failing_path)
{ {
bool readonly = (options & BIND_READONLY) != 0; bool readonly = (options & BIND_READONLY) != 0;
bool devices = (options & BIND_DEVICES) != 0; bool devices = (options & BIND_DEVICES) != 0;
@ -385,34 +387,76 @@ bind_mount (int proc_fd,
unsigned long current_flags, new_flags; unsigned long current_flags, new_flags;
cleanup_mount_tab MountTab mount_tab = NULL; cleanup_mount_tab MountTab mount_tab = NULL;
cleanup_free char *resolved_dest = 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; int i;
if (src) if (src)
{ {
if (mount (src, dest, NULL, MS_BIND | (recursive ? MS_REC : 0), NULL) != 0) if (mount (src, dest, NULL, MS_SILENT | MS_BIND | (recursive ? MS_REC : 0), NULL) != 0)
return 1; return BIND_MOUNT_ERROR_MOUNT;
} }
/* The mount operation will resolve any symlinks in the destination /* 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. */ path, so to find it in the mount table we need to do that too. */
resolved_dest = realpath (dest, NULL); resolved_dest = realpath (dest, NULL);
if (resolved_dest == NULL) if (resolved_dest == NULL)
return 2; return BIND_MOUNT_ERROR_REALPATH_DEST;
mount_tab = parse_mountinfo (proc_fd, resolved_dest); dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC);
if (mount_tab[0].mountpoint == NULL) if (dest_fd < 0)
{ {
errno = EINVAL; if (failing_path != NULL)
return 2; /* No mountpoint at dest */ *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; current_flags = mount_tab[0].options;
new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0); new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
if (new_flags != current_flags && if (new_flags != current_flags &&
mount ("none", resolved_dest, mount ("none", resolved_dest,
NULL, MS_BIND | MS_REMOUNT | new_flags, NULL) != 0) NULL, MS_SILENT | MS_BIND | MS_REMOUNT | new_flags, NULL) != 0)
return 3; {
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 /* 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. * 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); new_flags = current_flags | (devices ? 0 : MS_NODEV) | MS_NOSUID | (readonly ? MS_RDONLY : 0);
if (new_flags != current_flags && if (new_flags != current_flags &&
mount ("none", mount_tab[i].mountpoint, 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 /* 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. */ be safe to ignore because its not something the user can access. */
if (errno != EACCES) 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);
} }

View File

@ -1,5 +1,6 @@
/* bubblewrap /* bubblewrap
* Copyright (C) 2016 Alexander Larsson * Copyright (C) 2016 Alexander Larsson
* SPDX-License-Identifier: LGPL-2.0-or-later
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -18,13 +19,36 @@
#pragma once #pragma once
#include "utils.h"
typedef enum { typedef enum {
BIND_READONLY = (1 << 0), BIND_READONLY = (1 << 0),
BIND_DEVICES = (1 << 2), BIND_DEVICES = (1 << 2),
BIND_RECURSIVE = (1 << 3), BIND_RECURSIVE = (1 << 3),
} bind_option_t; } bind_option_t;
int bind_mount (int proc_fd, 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 *src,
const char *dest, const char *dest,
bind_option_t options); 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)));

File diff suppressed because it is too large Load Diff

BIN
bubblewrap.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

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

1421
build-aux/config.guess vendored

File diff suppressed because it is too large Load Diff

1807
build-aux/config.sub vendored

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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"]="" # Red.
color_map["grn"]="" # Green.
color_map["lgn"]="" # Light green.
color_map["blu"]="" # Blue.
color_map["mgn"]="" # Magenta.
color_map["std"]="" # 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:

View File

@ -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='' # Red.
grn='' # Green.
lgn='' # Light green.
blu='' # Blue.
mgn='' # Magenta.
std='' # 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
View File

@ -6,7 +6,7 @@
<refentryinfo> <refentryinfo>
<title>bwrap</title> <title>bwrap</title>
<productname>Project Atomic</productname> <productname>Containers</productname>
<authorgroup> <authorgroup>
<author> <author>
<contrib>Developer</contrib> <contrib>Developer</contrib>
@ -42,7 +42,8 @@
<refsect1><title>Description</title> <refsect1><title>Description</title>
<para> <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. are unlikely to use it directly from the commandline, although that is possible.
</para> </para>
<para> <para>
@ -91,6 +92,10 @@
multiple sources. multiple sources.
</para></listitem> </para></listitem>
</varlistentry> </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> </variablelist>
<para>Options related to kernel namespaces:</para> <para>Options related to kernel namespaces:</para>
<variablelist> <variablelist>
@ -130,16 +135,45 @@
<term><option>--unshare-all</option></term> <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> <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>
<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> <varlistentry>
<term><option>--userns <arg choice="plain">FD</arg></option></term> <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> <para>This is incompatible with --unshare-user, and doesn't work in the setuid version of bubblewrap.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>--userns2 <arg choice="plain">FD</arg></option></term> <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> <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>
<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> <varlistentry>
<term><option>--pidns <arg choice="plain">FD</arg></option></term> <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> <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> <term><option>--unsetenv <arg choice="plain">VAR</arg></option></term>
<listitem><para>Unset an environment variable</para></listitem> <listitem><para>Unset an environment variable</para></listitem>
</varlistentry> </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> </variablelist>
<para>Options for monitoring the sandbox from the outside:</para> <para>Options for monitoring the sandbox from the outside:</para>
<variablelist> <variablelist>
@ -190,10 +230,45 @@
<para> <para>
Filesystem related options. These are all operations that modify the filesystem directly, or 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. 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 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> </para>
<variablelist> <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> <varlistentry>
<term><option>--bind <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term> <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> <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>
<varlistentry> <varlistentry>
<term><option>--tmpfs <arg choice="plain">DEST</arg></option></term> <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>
<varlistentry> <varlistentry>
<term><option>--mqueue <arg choice="plain">DEST</arg></option></term> <term><option>--mqueue <arg choice="plain">DEST</arg></option></term>
@ -240,23 +321,68 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>--dir <arg choice="plain">DEST</arg></option></term> <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>
<varlistentry> <varlistentry>
<term><option>--file <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term> <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>
<varlistentry> <varlistentry>
<term><option>--bind-data <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term> <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>
<varlistentry> <varlistentry>
<term><option>--ro-bind-data <arg choice="plain">FD</arg> <arg choice="plain">DEST</arg></option></term> <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>
<varlistentry> <varlistentry>
<term><option>--symlink <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term> <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> </varlistentry>
</variablelist> </variablelist>
<para>Lockdown options:</para> <para>Lockdown options:</para>
@ -265,8 +391,25 @@
<term><option>--seccomp <arg choice="plain">FD</arg></option></term> <term><option>--seccomp <arg choice="plain">FD</arg></option></term>
<listitem><para> <listitem><para>
Load and use seccomp rules from <arg choice="plain">FD</arg>. 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. 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> </para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -303,6 +446,28 @@
Write information in JSON format about the sandbox to FD. Write information in JSON format about the sandbox to FD.
</para></listitem> </para></listitem>
</varlistentry> </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> <varlistentry>
<term><option>--new-session</option></term> <term><option>--new-session</option></term>
<listitem><para> <listitem><para>
@ -312,7 +477,9 @@
</para><para> </para><para>
Note: In a general sandbox, if you don't use --new-session, it is Note: In a general sandbox, if you don't use --new-session, it is
recommended to use seccomp to disallow the TIOCSTI ioctl, otherwise 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> </para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -333,7 +500,8 @@
<varlistentry> <varlistentry>
<term><option>--cap-add <arg choice="plain">CAP</arg></option></term> <term><option>--cap-add <arg choice="plain">CAP</arg></option></term>
<listitem><para> <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. the special value ALL to add all the permitted caps.
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>

111
ci/builddeps.sh Executable file
View File

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

View File

@ -1,5 +1,5 @@
#!/bin/bash # shellcheck shell=bash
#
# bash completion file for bubblewrap commands # bash completion file for bubblewrap commands
# #
@ -7,31 +7,39 @@ _bwrap() {
local cur prev words cword local cur prev words cword
_init_completion || return _init_completion || return
# Please keep sorted in LC_ALL=C order
local boolean_options=" local boolean_options="
--as-pid-1 --as-pid-1
--assert-userns-disabled
--clearenv
--disable-userns
--help --help
--new-session --new-session
--unshare-all
--unshare-cgroup --unshare-cgroup
--unshare-cgroup-try --unshare-cgroup-try
--unshare-user
--unshare-user-try
--unshare-all
--unshare-ipc --unshare-ipc
--unshare-net --unshare-net
--unshare-pid --unshare-pid
--unshare-user
--unshare-user-try
--unshare-uts --unshare-uts
--version --version
" "
# Please keep sorted in LC_ALL=C order
local options_with_args=" local options_with_args="
$boolean_optons $boolean_optons
--add-seccomp-fd
--args --args
--argv0
--bind --bind
--bind-data --bind-data
--block-fd --block-fd
--cap-add --cap-add
--cap-drop --cap-drop
--chdir --chdir
--chmod
--dev --dev
--dev-bind --dev-bind
--die-with-parent --die-with-parent
@ -43,11 +51,13 @@ _bwrap() {
--hostname --hostname
--info-fd --info-fd
--lock-file --lock-file
--perms
--proc --proc
--remount-ro --remount-ro
--ro-bind --ro-bind
--seccomp --seccomp
--setenv --setenv
--size
--symlink --symlink
--sync-fd --sync-fd
--uid --uid
@ -62,3 +72,5 @@ _bwrap() {
return 0 return 0
} }
complete -F _bwrap bwrap complete -F _bwrap bwrap
# vim:set ft=bash:

View File

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

7
completions/meson.build Normal file
View File

@ -0,0 +1,7 @@
if get_option('bash_completion').enabled()
subdir('bash')
endif
if get_option('zsh_completion').enabled()
subdir('zsh')
endif

115
completions/zsh/_bwrap Normal file
View File

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

View File

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

View File

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

7303
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
AC_PREREQ([2.63]) 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_HEADER([config.h])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
@ -14,6 +14,7 @@ AC_SYS_LARGEFILE
AC_PROG_CC AC_PROG_CC
AM_PROG_CC_C_O AM_PROG_CC_C_O
PKG_PROG_PKG_CONFIG([])
AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])]) 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]) [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], PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
[BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"], [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"],
[BASH_COMPLETION_DIR="$datadir/bash-completion/completions"]) [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
else ],
[
BASH_COMPLETION_DIR="$with_bash_completion_dir" BASH_COMPLETION_DIR="$with_bash_completion_dir"
fi ])
AC_SUBST([BASH_COMPLETION_DIR]) AC_SUBST([BASH_COMPLETION_DIR])
AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"]) 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 have_selinux=no
AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support])) 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], PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9],
[AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available]) [AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available])
have_selinux=yes have_selinux=yes
M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"], M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"],
[have_selinux=no]) [have_selinux=no])
if test "x$have_selinux" = xno -a "x$enable_selinux" = xyes; then AS_IF([test "x$have_selinux" = xno && test "x$enable_selinux" = xyes],
AC_MSG_ERROR([*** SELinux support requested but libraries not found]) [AC_MSG_ERROR([*** SELinux support requested but libraries not found])])
fi PKG_CHECK_MODULES([SELINUX_2_3], [libselinux >= 2.3],
fi [AC_DEFINE(HAVE_SELINUX_2_3, 1, [Define if SELinux is version >= 2.3])],
[:])
])
AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"]) AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"])
dnl Keep this in sync with ostree, except remove -Werror=declaration-after-statement dnl Keep this in sync with ostree, except remove -Werror=declaration-after-statement
CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\ CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\
-pipe \ -pipe \
-Wall \ -Wall \
-Werror=shadow \
-Werror=empty-body \ -Werror=empty-body \
-Werror=strict-prototypes \ -Werror=strict-prototypes \
-Werror=missing-prototypes \ -Werror=missing-prototypes \
@ -84,14 +105,15 @@ CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\
-Werror=incompatible-pointer-types \ -Werror=incompatible-pointer-types \
-Werror=misleading-indentation \ -Werror=misleading-indentation \
-Werror=missing-include-dirs -Werror=aggregate-return \ -Werror=missing-include-dirs -Werror=aggregate-return \
-Werror=switch-default \
-Wswitch-enum \
]) ])
AC_SUBST(WARN_CFLAGS) AC_SUBST(WARN_CFLAGS)
AC_CHECK_LIB(cap, cap_from_text) AC_CHECK_LIB(cap, cap_from_text)
if test "$ac_cv_lib_cap_cap_from_text" != "yes"; then AS_IF([test "$ac_cv_lib_cap_cap_from_text" != "yes"],
AC_MSG_ERROR([*** libcap requested but not found]) [AC_MSG_ERROR([*** libcap requested but not found])])
fi
AC_ARG_WITH(priv-mode, AC_ARG_WITH(priv-mode,
AS_HELP_STRING([--with-priv-mode=setuid/none], AS_HELP_STRING([--with-priv-mode=setuid/none],

View File

@ -1 +0,0 @@
demos/*

24
debian/changelog vendored
View File

@ -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
debian/clean vendored
View File

@ -1 +0,0 @@
config.log

36
debian/control vendored
View File

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

68
debian/copyright vendored
View File

@ -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
debian/docs vendored
View File

@ -1 +0,0 @@
README.md

View File

@ -1,2 +0,0 @@
# this is known and intentional
bubblewrap: setuid-binary usr/bin/bwrap 4755 root/root

42
debian/rules vendored
View File

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

3
debian/salsa-ci.yml vendored
View File

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

View File

@ -1 +0,0 @@
3.0 (native)

21
debian/tests/basic vendored
View File

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

36
debian/tests/control vendored
View File

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

40
debian/tests/dev vendored
View File

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

24
debian/tests/net vendored
View File

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

View File

@ -1,5 +0,0 @@
#!/bin/sh
set -e
exec tests/test-run.sh

View File

@ -1,5 +0,0 @@
#!/bin/sh
set -e
exec tests/test-run.sh

View File

@ -1 +0,0 @@
upstream

42
debian/tests/userns vendored
View File

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

View File

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

3
debian/watch vendored
View File

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

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# For this to work you first have to run these commands: # For this to work you first have to run these commands:
# curl -O http://sdk.gnome.org/nightly/keys/nightly.gpg # 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/ # flatpak --user remote-add --gpg-key=nightly.gpg gnome-nightly http://sdk.gnome.org/nightly/repo/

View File

@ -24,7 +24,6 @@ else:
os.close(pipe_info[0]) os.close(pipe_info[0])
os.close(userns_block[1]) os.close(userns_block[1])
if sys.version_info >= (3, 4):
os.set_inheritable(pipe_info[1], True) os.set_inheritable(pipe_info[1], True)
os.set_inheritable(userns_block[0], True) os.set_inheritable(userns_block[0], True)

348
git.mk Normal file
View File

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

171
meson.build Normal file
View File

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

73
meson_options.txt Normal file
View File

@ -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 : '',
)

View File

@ -1,5 +1,6 @@
/* bubblewrap /* bubblewrap
* Copyright (C) 2016 Alexander Larsson * Copyright (C) 2016 Alexander Larsson
* SPDX-License-Identifier: LGPL-2.0-or-later
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -62,7 +63,7 @@ rtnl_send_request (int rtnl_fd,
static int static int
rtnl_read_reply (int rtnl_fd, rtnl_read_reply (int rtnl_fd,
int seq_nr) unsigned int seq_nr)
{ {
char buffer[1024]; char buffer[1024];
ssize_t received; ssize_t received;
@ -79,7 +80,7 @@ rtnl_read_reply (int rtnl_fd,
{ {
if (rheader->nlmsg_seq != seq_nr) if (rheader->nlmsg_seq != seq_nr)
return -1; return -1;
if (rheader->nlmsg_pid != getpid ()) if ((pid_t)rheader->nlmsg_pid != getpid ())
return -1; return -1;
if (rheader->nlmsg_type == NLMSG_ERROR) if (rheader->nlmsg_type == NLMSG_ERROR)
{ {
@ -129,7 +130,7 @@ rtnl_setup_request (char *buffer,
header->nlmsg_seq = counter++; header->nlmsg_seq = counter++;
header->nlmsg_pid = getpid (); header->nlmsg_pid = getpid ();
return (struct nlmsghdr *) header; return header;
} }
void void

View File

@ -1,5 +1,6 @@
/* bubblewrap /* bubblewrap
* Copyright (C) 2016 Alexander Larsson * Copyright (C) 2016 Alexander Larsson
* SPDX-License-Identifier: LGPL-2.0-or-later
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public

View File

@ -46,4 +46,3 @@ find $RPM_BUILD_ROOT -name '*.la' -delete
%{_bindir}/bwrap %{_bindir}/bwrap
%endif %endif
%{_mandir}/man1/* %{_mandir}/man1/*

18
release-checklist.md Normal file
View File

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

View File

@ -5,10 +5,13 @@
# #
# Known copies are in the following repos: # 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> # 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 # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public # modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either # 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 # 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) # (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 else
export LC_ALL=C export LC_ALL=C.UTF-8
fi fi
# A GNU extension, used whenever LC_ALL is not C
unset LANGUAGE
# This should really be the default IMO # This should really be the default IMO
export G_DEBUG=fatal-warnings export G_DEBUG=fatal-warnings
@ -119,10 +128,23 @@ assert_file_has_content () {
done done
} }
assert_file_has_content_literal () { assert_file_has_content_once () {
if ! grep -q -F -e "$2" "$1"; then fpath=$1
_fatal_print_file "$1" "File '$1' doesn't match fixed string list '$2'" 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 fi
done
}
assert_file_has_content_literal () {
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 () { assert_file_has_mode () {
@ -159,6 +181,10 @@ skip() {
exit 0 exit 0
} }
extract_child_pid() { report_err () {
grep child-pid "$1" | sed "s/^.*: \([0-9]*\).*/\1/" 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

115
tests/libtest.sh Normal file
View File

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

72
tests/meson.build Normal file
View File

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

View File

@ -1,106 +1,44 @@
#!/bin/bash #!/usr/bin/env bash
set -xeuo pipefail set -xeuo pipefail
# Make sure /sbin/getpcaps etc. are in our PATH even if non-root srcd=$(cd $(dirname "$0") && pwd)
PATH="$PATH:/usr/sbin:/sbin"
srcd=$(cd $(dirname $0) && pwd) . ${srcd}/libtest.sh
. ${srcd}/libtest-core.sh bn=$(basename "$0")
bn=$(basename $0) test_count=0
tempdir=$(mktemp -d /var/tmp/tap-test.XXXXXX) ok () {
touch ${tempdir}/.testtmp test_count=$((test_count + 1))
function cleanup () { echo ok $test_count "$@"
if test -n "${TEST_SKIP_CLEANUP:-}"; then }
echo "Skipping cleanup of ${test_tmpdir}" ok_skip () {
else if test -f ${tempdir}/.test; then ok "# SKIP" "$@"
rm "${tempdir}" -rf }
fi done_testing () {
fi 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 # Test help
${BWRAP} --help > help.txt ${BWRAP} --help > help.txt
assert_file_has_content help.txt "usage: ${BWRAP}" 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 # Test fuse fs as bind source
if [ x$FUSE_DIR != x ]; then if [ "x$FUSE_DIR" != "x" ]; then
$RUN $ALT --proc /proc --dev /dev --bind $FUSE_DIR /tmp/foo true $RUN $ALT --proc /proc --dev /dev --bind $FUSE_DIR /tmp/foo true
echo "ok - can bind-mount a FUSE directory with $ALT" ok "can bind-mount a FUSE directory with $ALT"
else else
echo "ok # SKIP no FUSE support" ok_skip "no FUSE support"
fi fi
# no --dev => no devpts => no map_root workaround # no --dev => no devpts => no map_root workaround
$RUN $ALT --proc /proc true $RUN $ALT --proc /proc true
echo "ok - can mount /proc with $ALT" ok "can mount /proc with $ALT"
# No network # No network
$RUN $ALT --unshare-net --proc /proc --dev /dev true $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 # Unreadable file
echo -n "expect EPERM: " >&2 echo -n "expect EPERM: " >&2
@ -111,34 +49,56 @@ for ALT in "" "--unshare-user-try" "--unshare-pid" "--unshare-user-try --unshar
CAP="" CAP=""
fi 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 assert_not_reached Could read /etc/shadow
fi fi
echo "ok - cannot read /etc/shadow with $ALT"
ok "cannot read /etc/shadow with $ALT"
# Unreadable dir # Unreadable dir
if [ x$UNREADABLE != x ]; then if [ "x$UNREADABLE" != "x" ]; then
echo -n "expect EPERM: " >&2 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 assert_not_reached Could read $UNREADABLE
fi fi
echo "ok - cannot read $UNREADABLE with $ALT" ok "cannot read $UNREADABLE with $ALT"
else else
echo "ok # SKIP not sure what unreadable file to use" ok_skip "not sure what unreadable file to use"
fi fi
# bind dest in symlink (https://github.com/projectatomic/bubblewrap/pull/119) # 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 $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 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 # Test devices
$RUN --unshare-pid --dev /dev ls -al /dev/{stdin,stdout,stderr,null,random,urandom,fd,core} >/dev/null $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 # Test --as-pid-1
$RUN --unshare-pid --as-pid-1 --bind / / bash -c 'echo $$' > as_pid_1.txt $RUN --unshare-pid --as-pid-1 --bind / / bash -c 'echo $$' > as_pid_1.txt
assert_file_has_content as_pid_1.txt "1" 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 # 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 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 info.json '"child-pid": [0-9]'
assert_file_has_content json-status.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' 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) 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,34 +118,55 @@ for NS in "ipc" "mnt" "net" "pid" "uts"; do
assert_file_has_content json-status.json "$want" assert_file_has_content json-status.json "$want"
done 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 if ! command -v strace >/dev/null || ! strace -h | grep -v -e default | grep -e fault >/dev/null; then
echo "ok - # SKIP no strace fault injection" ok_skip "no strace fault injection"
else 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 ! 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]' 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 fi
notanexecutable=/ notanexecutable=/
$RUN --json-status-fd 42 $notanexecutable 42>json-status.json || true $RUN --json-status-fd 42 $notanexecutable 42>json-status.json || true
assert_not_file_has_content json-status.json '"exit-code": [0-9]' 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 # These tests require --unshare-user
if test -n "${bwrap_is_suid:-}"; then if test -n "${bwrap_is_suid:-}"; then
echo "ok - # SKIP no --cap-add support" ok_skip "no --cap-add support"
echo "ok - # SKIP no --cap-add support" ok_skip "no --cap-add support"
ok_skip "no --disable-userns"
else else
BWRAP_RECURSE="$BWRAP --unshare-all --uid 0 --gid 0 --cap-add ALL --bind / / --bind /proc /proc" BWRAP_RECURSE="$BWRAP --unshare-user --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-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" 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 fi
# Test error prefixing # Test error prefixing
@ -193,23 +174,24 @@ if $RUN --unshare-pid --bind /source-enoent /dest true 2>err.txt; then
assert_not_reached "bound nonexistent source" assert_not_reached "bound nonexistent source"
fi fi
assert_file_has_content err.txt "^bwrap: Can't find source path.*source-enoent" 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 if ! ${is_uidzero}; then
# When invoked as non-root, check that by default we have no caps left # 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 for OPT in "" "--unshare-user-try --as-pid-1" "--unshare-user-try" "--as-pid-1"; do
e=0 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 sed -e 's/^/# /' < caps.test >&2
test "$e" = 0 test "$e" = 0
assert_not_file_has_content caps.test ': =.*cap' assert_not_file_has_content caps.test ': =.*cap'
done done
echo "ok - we have no caps as uid != 0" ok "we have no caps as uid != 0"
else 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 for OPT in "" "--as-pid-1"; do
$RUN $OPT --unshare-pid capsh --print >caps.test $RUN $OPT --unshare-pid capsh --print >caps.test
diff -u caps.orig caps.test diff -u caps.expected caps.test
done done
# And test that we can drop all, as well as specific caps # And test that we can drop all, as well as specific caps
$RUN $OPT --cap-drop ALL --unshare-pid capsh --print >caps.test $RUN $OPT --cap-drop ALL --unshare-pid capsh --print >caps.test
@ -227,7 +209,7 @@ else
assert_file_has_content caps.test '^Current: =eip.*cap_fowner.*-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$' assert_not_file_has_content caps.test '^Current: =.*cap_net_bind_service.*-eip$'
fi fi
echo "ok - we have the expected caps as uid 0" ok "we have the expected caps as uid 0"
fi fi
# Test --die-with-parent # 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 # 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 # another process is holding it. If we're unlucky, lockf-n.py will
# be holding it. # 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=$! childshellpid=$!
# Wait for lock to be taken (yes hacky) # 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} kill -9 ${childshellpid}
# Lock file should be unlocked # Lock file should be unlocked
./lockf-n.py ./lock wait ./lockf-n.py ./lock wait
echo "ok die with parent ${die_with_parent_argv}" ok "die with parent ${die_with_parent_argv}"
done done
printf '%s--dir\0/tmp/hello/world\0' '' > test.args printf '%s--dir\0/tmp/hello/world\0' '' > test.args
$RUN --args 3 test -d /tmp/hello/world 3<test.args printf '%s--dir\0/tmp/hello/world2\0' '' > test.args2
echo "ok - we can parse arguments from a fd" 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 mkdir bin
echo "#!/bin/sh" > bin/--inadvisable-executable-name-- echo "#!/bin/sh" > bin/--inadvisable-executable-name--
@ -287,35 +271,35 @@ echo "echo hello" >> bin/--inadvisable-executable-name--
chmod +x bin/--inadvisable-executable-name-- chmod +x bin/--inadvisable-executable-name--
PATH="${srcd}:$PATH" $RUN -- sh -c "echo hello" > stdout PATH="${srcd}:$PATH" $RUN -- sh -c "echo hello" > stdout
assert_file_has_content stdout hello 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 PATH="$(pwd)/bin:$PATH" $RUN -- --inadvisable-executable-name-- > stdout
assert_file_has_content stdout hello 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 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" assert_not_reached "'--dev-bind' should have been interpreted as a (silly) executable name"
fi 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 if command -v mktemp > /dev/null; then
tempfile="$(mktemp /tmp/bwrap-test-XXXXXXXX)" tempfile="$(mktemp /tmp/bwrap-test-XXXXXXXX)"
echo "hello" > "$tempfile" echo "hello" > "$tempfile"
$BWRAP --bind / / cat "$tempfile" > stdout $BWRAP --bind / / cat "$tempfile" > stdout
assert_file_has_content stdout hello 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 $BWRAP --bind / / --bind /tmp /tmp cat "$tempfile" > stdout
assert_file_has_content stdout hello assert_file_has_content stdout hello
echo "ok - bind-mount of /tmp exposes real /tmp" ok "bind-mount of /tmp exposes real /tmp"
if [ -d /mnt ]; then if [ -d /mnt ] && [ ! -L /mnt ]; then
$BWRAP --bind / / --bind /tmp /mnt cat "/mnt/${tempfile#/tmp/}" > stdout $BWRAP --bind / / --bind /tmp /mnt cat "/mnt/${tempfile#/tmp/}" > stdout
assert_file_has_content stdout hello 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 else
echo "ok - # SKIP /mnt does not exist" ok_skip "/mnt does not exist or is a symlink"
fi fi
else else
echo "ok - # SKIP mktemp not found" ok_skip "mktemp not found"
echo "ok - # SKIP mktemp not found" ok_skip "mktemp not found"
echo "ok - # SKIP mktemp not found" ok_skip "mktemp not found"
fi fi
if $RUN test -d /tmp/oldroot; then if $RUN test -d /tmp/oldroot; then
@ -334,7 +318,7 @@ fi
if $BWRAP --bind / / --bind "$(pwd)" /tmp test -d /tmp/newroot; then if $BWRAP --bind / / --bind "$(pwd)" /tmp test -d /tmp/newroot; then
assert_not_reached "/tmp/newroot should not be visible" assert_not_reached "/tmp/newroot should not be visible"
fi fi
echo "ok - we can mount another directory onto /tmp" ok "we can mount another directory onto /tmp"
echo "hello" > input.$$ echo "hello" > input.$$
$RUN --bind "$(pwd)" /tmp/here cat /tmp/here/input.$$ > stdout $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 if $RUN --bind "$(pwd)" /tmp/here test -d /tmp/newroot; then
assert_not_reached "/tmp/newroot should not be visible" assert_not_reached "/tmp/newroot should not be visible"
fi fi
echo "ok - we can mount another directory inside /tmp" ok "we can mount another directory inside /tmp"
# These tests need user namespaces touch some-file
if test -n "${bwrap_is_suid:-}"; then mkdir -p some-dir
echo "ok - # SKIP no setuid support for --unshare-user" rm -fr new-dir-mountpoint
echo "ok - # SKIP no setuid support for --unshare-user" 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 else
mkfifo donepipe ok_skip "- /dev/log is not a socket, cannot test bubblewrap#409"
$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"
fi 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

635
tests/test-seccomp.py Executable file
View File

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

28
tests/test-specifying-pidns.sh Executable file
View File

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

28
tests/test-specifying-userns.sh Executable file
View File

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

215
tests/test-utils.c Normal file
View File

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

180
tests/try-syscall.c Normal file
View File

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

2
tests/use-as-subproject/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/_build/
/subprojects/

View File

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

View File

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

View File

@ -0,0 +1 @@
#error Should not use superproject config.h to compile bubblewrap

View File

@ -0,0 +1 @@
#error Should not use superproject generated config.h to compile bubblewrap

View File

@ -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-',
],
)

179
utils.c
View File

@ -1,5 +1,6 @@
/* bubblewrap /* bubblewrap
* Copyright (C) 2016 Alexander Larsson * Copyright (C) 2016 Alexander Larsson
* SPDX-License-Identifier: LGPL-2.0-or-later
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -18,28 +19,72 @@
#include "config.h" #include "config.h"
#include "utils.h" #include "utils.h"
#include <limits.h>
#include <stdint.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/socket.h> #include <sys/socket.h>
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
#include <selinux/selinux.h> #include <selinux/selinux.h>
#endif #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 void
die_with_error (const char *format, ...) die_with_error (const char *format, ...)
{ {
va_list args; va_list args;
int errsv; 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; errsv = errno;
va_start (args, format); va_start (args, format);
vfprintf (stderr, format, args); warnv (format, args, mount_strerror (errsv));
va_end (args); va_end (args);
fprintf (stderr, ": %s\n", strerror (errsv));
exit (1); exit (1);
} }
@ -48,24 +93,20 @@ die (const char *format, ...)
{ {
va_list args; va_list args;
fprintf (stderr, "bwrap: ");
va_start (args, format); va_start (args, format);
vfprintf (stderr, format, args); warnv (format, args, NULL);
va_end (args); va_end (args);
fprintf (stderr, "\n");
exit (1); exit (1);
} }
void void
die_unless_label_valid (const char *label) die_unless_label_valid (UNUSED const char *label)
{ {
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
if (is_selinux_enabled () == 1) 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); die_with_error ("invalid label %s", label);
return; return;
} }
@ -104,9 +145,9 @@ xmalloc (size_t size)
} }
void * 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) if (res == NULL)
die_oom (); die_oom ();
@ -116,9 +157,13 @@ xcalloc (size_t size)
void * void *
xrealloc (void *ptr, size_t size) 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 (); die_oom ();
return res; return res;
} }
@ -230,6 +275,13 @@ has_prefix (const char *str,
return strncmp (str, prefix, strlen (prefix)) == 0; return strncmp (str, prefix, strlen (prefix)) == 0;
} }
void
xclearenv (void)
{
if (clearenv () != 0)
die_with_error ("clearenv failed");
}
void void
xsetenv (const char *name, const char *value, int overwrite) 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 /* We check this ahead of time, otherwise
the create file will fail in the read-only 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 && if (stat (path, &buf) == 0 &&
S_ISREG (buf.st_mode)) !S_ISDIR (buf.st_mode) &&
!S_ISLNK (buf.st_mode))
return 0; return 0;
if (create_file (path, mode, NULL) != 0 && errno != EEXIST) 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_read;
ssize_t data_len; ssize_t data_len;
ssize_t res; ssize_t res;
int errsv;
data_read = 0; data_read = 0;
data_len = 4080; data_len = 4080;
@ -544,6 +600,12 @@ load_file_data (int fd,
{ {
if (data_len == data_read + 1) if (data_len == data_read + 1)
{ {
if (data_len > SSIZE_MAX / 2)
{
errno = EFBIG;
return NULL;
}
data_len *= 2; data_len *= 2;
data = xrealloc (data, data_len); data = xrealloc (data, data_len);
} }
@ -553,12 +615,7 @@ load_file_data (int fd,
while (res < 0 && errno == EINTR); while (res < 0 && errno == EINTR);
if (res < 0) if (res < 0)
{
errsv = errno;
close (fd);
errno = errsv;
return NULL; return NULL;
}
data_read += res; data_read += res;
} }
@ -638,7 +695,7 @@ ensure_dir (const char *path,
/* Sets errno on error (!= 0) */ /* Sets errno on error (!= 0) */
int int
mkdir_with_parents (const char *pathname, mkdir_with_parents (const char *pathname,
int mode, mode_t mode,
bool create_last) bool create_last)
{ {
cleanup_free char *fn = NULL; cleanup_free char *fn = NULL;
@ -745,7 +802,7 @@ read_pid_from_socket (int socket)
msg.msg_controllen = control_len_rcv; msg.msg_controllen = control_len_rcv;
if (recvmsg (socket, &msg, 0) < 0) 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) if (msg.msg_controllen <= 0)
die ("Unexpected short read from pid socket"); die ("Unexpected short read from pid socket");
@ -764,6 +821,47 @@ read_pid_from_socket (int socket)
die ("No pid returned on 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 int
raw_clone (unsigned long flags, raw_clone (unsigned long flags,
void *child_stack) void *child_stack)
@ -789,7 +887,7 @@ pivot_root (const char * new_root, const char * put_old)
} }
char * char *
label_mount (const char *opt, const char *mount_label) label_mount (const char *opt, UNUSED const char *mount_label)
{ {
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
if (mount_label) if (mount_label)
@ -806,21 +904,42 @@ label_mount (const char *opt, const char *mount_label)
} }
int int
label_create_file (const char *file_label) label_create_file (UNUSED const char *file_label)
{ {
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
if (is_selinux_enabled () > 0 && file_label) if (is_selinux_enabled () > 0 && file_label)
return setfscreatecon ((security_context_t) file_label); return setfscreatecon (file_label);
#endif #endif
return 0; return 0;
} }
int int
label_exec (const char *exec_label) label_exec (UNUSED const char *exec_label)
{ {
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
if (is_selinux_enabled () > 0 && exec_label) if (is_selinux_enabled () > 0 && exec_label)
return setexeccon ((security_context_t) exec_label); return setexeccon (exec_label);
#endif #endif
return 0; 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
View File

@ -1,5 +1,6 @@
/* bubblewrap /* bubblewrap
* Copyright (C) 2016 Alexander Larsson * Copyright (C) 2016 Alexander Larsson
* SPDX-License-Identifier: LGPL-2.0-or-later
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * 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_READ_END 0
#define PIPE_WRITE_END 1 #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, void die_with_error (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2))); ...) __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, void die (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2))); ...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
void die_oom (void) __attribute__((__noreturn__)); void die_oom (void) __attribute__((__noreturn__));
@ -57,11 +66,12 @@ void die_unless_label_valid (const char *label);
void fork_intermediate_child (void); void fork_intermediate_child (void);
void *xmalloc (size_t size); void *xmalloc (size_t size);
void *xcalloc (size_t size); void *xcalloc (size_t nmemb, size_t size);
void *xrealloc (void *ptr, void *xrealloc (void *ptr,
size_t size); size_t size);
char *xstrdup (const char *str); char *xstrdup (const char *str);
void strfreev (char **str_array); void strfreev (char **str_array);
void xclearenv (void);
void xsetenv (const char *name, void xsetenv (const char *name,
const char *value, const char *value,
int overwrite); int overwrite);
@ -107,11 +117,14 @@ int ensure_dir (const char *path,
mode_t mode); mode_t mode);
int get_file_mode (const char *pathname); int get_file_mode (const char *pathname);
int mkdir_with_parents (const char *pathname, int mkdir_with_parents (const char *pathname,
int mode, mode_t mode,
bool create_last); bool create_last);
void create_pid_socketpair (int sockets[2]); void create_pid_socketpair (int sockets[2]);
void send_pid_on_socket (int socket); void send_pid_on_socket (int socket);
int read_pid_from_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 */ /* syscall wrappers */
int raw_clone (unsigned long flags, 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_exec (const char *exec_label);
int label_create_file (const char *file_label); int label_create_file (const char *file_label);
const char *mount_strerror (int errsv);
static inline void static inline void
cleanup_freep (void *p) cleanup_freep (void *p)
{ {