Compare commits

1 Commits

Author SHA1 Message Date
9843f9b2b5 enable debug logging and add a bunch more tracing 2024-01-26 04:24:54 +00:00
24 changed files with 1298 additions and 1068 deletions

View File

@@ -9,12 +9,72 @@ on:
- 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@v4
uses: actions/checkout@v1
- name: Install build-dependencies
run: sudo ./ci/builddeps.sh
- name: Create logs dir
@@ -69,7 +129,7 @@ jobs:
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@v4
uses: actions/upload-artifact@v1
if: failure() || cancelled()
with:
name: test logs
@@ -85,19 +145,23 @@ jobs:
- cpp
steps:
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Check out
uses: actions/checkout@v4
uses: actions/checkout@v1
- name: Install build-dependencies
run: sudo ./ci/builddeps.sh --clang
- run: meson build -Dselinux=enabled
- name: autogen.sh
run: NOCONFIGURE=1 ./autogen.sh
- name: configure
run: ./configure --enable-selinux
env:
CC: clang
CFLAGS: >-
-O2
-Werror=unused-variable
- run: meson compile -C build
- name: make
run: make -j $(getconf _NPROCESSORS_ONLN) V=1
- name: CodeQL analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v1

12
Makefile-bwrap.am Normal file
View File

@@ -0,0 +1,12 @@
bwrap_SOURCES = \
$(bwrap_srcpath)/bubblewrap.c \
$(bwrap_srcpath)/bind-mount.h \
$(bwrap_srcpath)/bind-mount.c \
$(bwrap_srcpath)/network.h \
$(bwrap_srcpath)/network.c \
$(bwrap_srcpath)/utils.h \
$(bwrap_srcpath)/utils.c \
$(NULL)
bwrap_CFLAGS = $(AM_CFLAGS)
bwrap_LDADD = $(SELINUX_LIBS)

18
Makefile-docs.am Normal file
View File

@@ -0,0 +1,18 @@
XSLTPROC = xsltproc
XSLTPROC_FLAGS = \
--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
.xml.1:
$(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
if ENABLE_MAN
man_MANS = bwrap.1
CLEANFILES += $(man_MANS)
endif
EXTRA_DIST += bwrap.xml

102
Makefile.am Normal file
View File

@@ -0,0 +1,102 @@
AM_CFLAGS = $(WARN_CFLAGS)
CLEANFILES =
EXTRA_DIST = \
.dir-locals.el \
.editorconfig \
README.md \
autogen.sh \
completions/bash/meson.build \
completions/meson.build \
completions/zsh/meson.build \
demos/bubblewrap-shell.sh \
demos/flatpak-run.sh \
demos/flatpak.bpf \
demos/userns-block-fd.py \
meson.build \
meson_options.txt \
packaging/bubblewrap.spec \
tests/meson.build \
tests/use-as-subproject/README \
tests/use-as-subproject/config.h \
tests/use-as-subproject/dummy-config.h.in \
tests/use-as-subproject/meson.build \
uncrustify.cfg \
uncrustify.sh \
$(NULL)
GITIGNOREFILES = build-aux/ gtk-doc.make config.h.in aclocal.m4
bin_PROGRAMS = bwrap
bwrap_srcpath := $(srcdir)
include Makefile-bwrap.am
install-exec-hook:
if PRIV_MODE_SETUID
$(SUDO_BIN) chown root $(DESTDIR)$(bindir)/bwrap
$(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap
endif
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
rm -rf test-bwrap
cp bwrap test-bwrap
chmod 0755 test-bwrap
if PRIV_MODE_SETUID
$(SUDO_BIN) chown root test-bwrap
$(SUDO_BIN) chmod u+s test-bwrap
endif
tests_test_utils_SOURCES = \
tests/test-utils.c \
utils.h \
utils.c \
$(NULL)
tests_test_utils_LDADD = $(SELINUX_LIBS)
test_bwrap_SOURCES=
include Makefile-docs.am
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
LOG_COMPILER =
TESTS_ENVIRONMENT = \
BWRAP=$(abs_top_builddir)/test-bwrap \
G_TEST_BUILDDIR=$(abs_top_builddir) \
G_TEST_SRCDIR=$(abs_top_srcdir) \
$(NULL)
check_PROGRAMS = $(test_programs) $(test_extra_programs)
TESTS = $(test_programs) $(test_scripts)
EXTRA_DIST += $(test_scripts)
EXTRA_DIST += tests/libtest-core.sh
EXTRA_DIST += tests/libtest.sh
if ENABLE_BASH_COMPLETION
bashcompletiondir = $(BASH_COMPLETION_DIR)
dist_bashcompletion_DATA = completions/bash/bwrap
endif
if ENABLE_ZSH_COMPLETION
zshcompletiondir = $(ZSH_COMPLETION_DIR)
dist_zshcompletion_DATA = completions/zsh/_bwrap
endif
-include $(top_srcdir)/git.mk
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-bash-completion-dir="\$(datadir)"/bash-completion/ \
$(NULL)

51
NEWS.md
View File

@@ -1,51 +0,0 @@
bubblewrap 0.11.0
=================
Released: 2024-10-30
Dependencies:
* Remove the Autotools build system. Meson ≥ 0.49.0 is now required
at build-time. (#625, Hugo Osvaldo Barrera)
* For users of bash-completion, bash-completion ≥ 2.10 is recommended.
With older bash-completion, bubblewrap might install completions
outside its `${prefix}` unless overridden with `-Dbash_completion_dir=…`.
Enhancements:
* New `--overlay`, `--tmp-overlay`, `--ro-overlay` and `--overlay-src`
options allow creation of overlay mounts.
This feature is not available when bubblewrap is installed setuid.
(#412, #663; Ryan Hendrickson, William Manley, Simon McVittie)
* New `--level-prefix` option produces output that can be parsed by
tools like `logger --prio-prefix` and `systemd-cat --level-prefix=1`
(#646, Simon McVittie)
Bug fixes:
* Handle `EINTR` when doing I/O on files or sockets (#657, Simon McVittie)
* Don't make assumptions about alignment of socket control message data
(#637, Simon McVittie)
* Silence some Meson deprecation warnings (#647, @Sertonix)
* Update URLs in documentation to https (#566, @TotalCaesar659)
* Improve tests' compatibility with busybox (#627, @Sertonix)
* Improve compatibility with Meson < 1.3.0 (#664, Simon McVittie)
Internal changes:
* Consistently use `<stdbool.h>` for booleans (#660, Simon McVittie)
* Avoid `-Wshadow` compiler warnings (#661, Simon McVittie)
* Update Github Actions configuration (#658, Simon McVittie)
----
See also <https://github.com/containers/bubblewrap/releases>

View File

@@ -68,16 +68,13 @@ 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
-----
This program can be shared by all container tools which perform
non-root operation, such as:
- [Flatpak](https://www.flatpak.org)
- [Flatpak](http://www.flatpak.org)
- [rpm-ostree unprivileged](https://github.com/projectatomic/rpm-ostree/pull/209)
- [bwrap-oci](https://github.com/projectatomic/bwrap-oci)
@@ -92,15 +89,25 @@ 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:
If you need to build bubblewrap from source, you can do this with meson or autotools.
```sh
meson:
```
meson _builddir
meson compile -C _builddir
meson test -C _builddir
meson install -C _builddir
```
autotools:
```
./autogen.sh
make
sudo make install
```
Usage
-----
@@ -145,50 +152,26 @@ Any such directories you specify mounted `nodev` by default, and can be made rea
Additionally you can use these kernel features:
User namespaces ([CLONE_NEWUSER](https://linux.die.net/man/2/clone)): This hides all but the current uid and gid from the
User namespaces ([CLONE_NEWUSER](http://linux.die.net/man/2/clone)): This hides all but the current uid and gid from the
sandbox. You can also change what the value of uid/gid should be in the sandbox.
IPC namespaces ([CLONE_NEWIPC](https://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the
IPC namespaces ([CLONE_NEWIPC](http://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the
different forms of IPCs, like SysV shared memory and semaphores.
PID namespaces ([CLONE_NEWPID](https://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/).
PID namespaces ([CLONE_NEWPID](http://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/).
Network namespaces ([CLONE_NEWNET](https://linux.die.net/man/2/clone)): The sandbox will not see the network. Instead it will have its own network namespace with only a loopback device.
Network namespaces ([CLONE_NEWNET](http://linux.die.net/man/2/clone)): The sandbox will not see the network. Instead it will have its own network namespace with only a loopback device.
UTS namespace ([CLONE_NEWUTS](https://linux.die.net/man/2/clone)): The sandbox will have its own hostname.
UTS namespace ([CLONE_NEWUTS](http://linux.die.net/man/2/clone)): The sandbox will have its own hostname.
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,
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
------------------------------------

19
autogen.sh Executable file
View File

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

View File

@@ -76,7 +76,7 @@ match_token (const char *token, const char *token_end, const char *str)
if (token == token_end)
return *str == 0;
return false;
return FALSE;
}
static unsigned long
@@ -247,7 +247,7 @@ parse_mountinfo (int proc_fd,
die_with_error ("Can't open /proc/self/mountinfo");
n_lines = count_lines (mountinfo);
lines = xcalloc (n_lines, sizeof (MountInfoLine));
lines = xcalloc (n_lines * sizeof (MountInfoLine));
max_id = 0;
line = mountinfo;
@@ -281,12 +281,12 @@ parse_mountinfo (int proc_fd,
die ("Can't parse mountinfo line");
rest = line + consumed;
rest = skip_token (rest, true); /* mountroot */
rest = skip_token (rest, TRUE); /* mountroot */
mountpoint = rest;
rest = skip_token (rest, false); /* mountpoint */
rest = skip_token (rest, FALSE); /* mountpoint */
mountpoint_end = rest++;
options = rest;
rest = skip_token (rest, false); /* vfs options */
rest = skip_token (rest, FALSE); /* vfs options */
options_end = rest;
*mountpoint_end = 0;
@@ -310,11 +310,11 @@ parse_mountinfo (int proc_fd,
if (root == -1)
{
mount_tab = xcalloc (1, sizeof (MountInfo));
mount_tab = xcalloc (sizeof (MountInfo) * (1));
return steal_pointer (&mount_tab);
}
by_id = xcalloc (max_id + 1, sizeof (MountInfoLine*));
by_id = xcalloc ((max_id + 1) * sizeof (MountInfoLine*));
for (i = 0; i < n_lines; i++)
by_id[lines[i].id] = &lines[i];
@@ -324,7 +324,7 @@ parse_mountinfo (int proc_fd,
MountInfoLine *parent = by_id[this->parent_id];
MountInfoLine **to_sibling;
MountInfoLine *sibling;
bool covered = false;
bool covered = FALSE;
if (!has_path_prefix (this->mountpoint, root_mount))
continue;
@@ -333,7 +333,7 @@ parse_mountinfo (int proc_fd,
continue;
if (strcmp (parent->mountpoint, this->mountpoint) == 0)
parent->covered = true;
parent->covered = TRUE;
to_sibling = &parent->first_child;
sibling = parent->first_child;
@@ -344,7 +344,7 @@ parse_mountinfo (int proc_fd,
* covered by the sibling, and we drop it. */
if (has_path_prefix (this->mountpoint, sibling->mountpoint))
{
covered = true;
covered = TRUE;
break;
}
@@ -366,7 +366,7 @@ parse_mountinfo (int proc_fd,
}
n_mounts = count_mounts (&lines[root]);
mount_tab = xcalloc (n_mounts + 1, sizeof (MountInfo));
mount_tab = xcalloc (sizeof (MountInfo) * (n_mounts + 1));
end_tab = collect_mounts (&mount_tab[0], &lines[root]);
assert (end_tab == &mount_tab[n_mounts]);
@@ -405,7 +405,7 @@ bind_mount (int proc_fd,
if (resolved_dest == NULL)
return BIND_MOUNT_ERROR_REALPATH_DEST;
dest_fd = TEMP_FAILURE_RETRY (open (resolved_dest, O_PATH | O_CLOEXEC));
dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC);
if (dest_fd < 0)
{
if (failing_path != NULL)
@@ -499,7 +499,7 @@ bind_mount_result_to_string (bind_mount_result res,
bool *want_errno_p)
{
char *string = NULL;
bool want_errno = true;
bool want_errno = TRUE;
switch (res)
{
@@ -521,7 +521,7 @@ bind_mount_result_to_string (bind_mount_result res,
case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
string = xasprintf ("Unable to find \"%s\" in mount table", failing_path);
want_errno = false;
want_errno = FALSE;
break;
case BIND_MOUNT_ERROR_REMOUNT_DEST:
@@ -557,12 +557,9 @@ die_with_bind_result (bind_mount_result res,
...)
{
va_list args;
bool want_errno = true;
bool want_errno = TRUE;
char *message;
if (bwrap_level_prefix)
fprintf (stderr, "<%d>", LOG_ERR);
fprintf (stderr, "bwrap: ");
va_start (args, format);
@@ -574,24 +571,7 @@ die_with_bind_result (bind_mount_result res,
/* 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, ": %s", strerror (saved_errno));
fprintf (stderr, "\n");
exit (1);

File diff suppressed because it is too large Load Diff

111
bwrap.xml
View File

@@ -96,26 +96,6 @@
<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>
<varlistentry>
<term><option>--level-prefix</option></term>
<listitem>
<para>
Prefix each line of diagnostic output with a numeric severity
level enclosed in angle brackets.
The severity levels used are based on the constants used by
<citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>:
for example, <literal>&lt;4&gt;</literal> indicates a warning,
because <literal>LOG_WARNING</literal> has numeric value 4.
Numbers smaller than 4 indicate fatal errors, and numbers larger
than 4 indicate informational messages.
These prefixes can be parsed by tools compatible with
<literal>logger --prio-prefix</literal> (see
<citerefentry><refentrytitle>logger</refentrytitle><manvolnum>1</manvolnum></citerefentry>)
or <literal>systemd-cat --level-prefix=1</literal> (see
<citerefentry><refentrytitle>systemd-cat</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
</para>
</listitem>
</varlistentry>
</variablelist>
<para>Options related to kernel namespaces:</para>
<variablelist>
@@ -317,86 +297,6 @@
<term><option>--remount-ro <arg choice="plain">DEST</arg></option></term>
<listitem><para>Remount the path <arg choice="plain">DEST</arg> as readonly. It works only on the specified mount point, without changing any other mount point under the specified path</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--overlay-src <arg choice="plain">SRC</arg></option></term>
<listitem>
<para>
This option does nothing on its own, and must be followed by one of
the other <literal>overlay</literal> options. It specifies a host
path from which files should be read if they aren't present in a
higher layer.
</para>
<para>
This option can be used multiple times to provide multiple sources.
The sources are overlaid in the order given, with the first source on
the command line at the bottom of the stack: if a given path to be
read exists in more than one source, the file is read from the last
such source specified.
</para>
<para>
(For readers familiar with overlayfs, note that this is the
reverse of the order used by the kernel's <literal>lowerdir</literal>
mount option.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--overlay <arg choice="plain">RWSRC</arg> <arg choice="plain">WORKDIR</arg> <arg choice="plain">DEST</arg></option></term>
</varlistentry>
<varlistentry>
<term><option>--tmp-overlay <arg choice="plain">DEST</arg></option></term>
</varlistentry>
<varlistentry>
<term><option>--ro-overlay <arg choice="plain">DEST</arg></option></term>
<listitem>
<para>
Use overlayfs to mount the host paths specified by
<arg choice="plain">RWSRC</arg> and all immediately preceding
<option>--overlay-src</option> on <arg choice="plain">DEST</arg>.
<arg choice="plain">DEST</arg> will contain the union of all the files
in all the layers.
</para>
<para>
With <arg choice="plain">--overlay</arg> all writes will go to
<arg choice="plain">RWSRC</arg>. Reads will come preferentially from
<arg choice="plain">RWSRC</arg>, and then from any
<option>--overlay-src</option> paths.
<arg choice="plain">WORKDIR</arg> must be an empty directory on the
same filesystem as <arg choice="plain">RWSRC</arg>, and is used
internally by the kernel.
</para>
<para>
With <arg choice="plain">--tmp-overlay</arg> all writes will go to
the tmpfs that hosts the sandbox root, in a location not accessible
from either the host or the child process. Writes will therefore not
be persisted across multiple runs.
</para>
<para>
With <arg choice="plain">--ro-overlay</arg> the filesystem will be
mounted read-only. This option requires at least two
<option>--overlay-src</option> to precede it.
</para>
<para>
None of these options are available in the setuid version of
bubblewrap. Using <arg choice="plain">--ro-overlay</arg> or providing
more than one <option>--overlay-src</option> requires a Linux kernel
version of 4.0 or later.
</para>
<para>
Due to limitations of overlayfs, no host directory given via
<arg choice="plain">--overlay-src</arg> or
<arg choice="plain">--overlay</arg> may be an ancestor of another,
after resolving symlinks. Depending on version, the Linux kernel may
or may not enforce this, but if not then overlayfs's behavior is
undefined.
</para>
<para>
For more information see the Overlay Filesystem documentation in the
Linux kernel at
https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--proc <arg choice="plain">DEST</arg></option></term>
<listitem><para>Mount procfs on <arg choice="plain">DEST</arg></para></listitem>
@@ -464,16 +364,7 @@
</varlistentry>
<varlistentry>
<term><option>--symlink <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
<listitem>
<para>Create a symlink at <arg choice="plain">DEST</arg> with target
<arg choice="plain">SRC</arg>.</para>
<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>
<listitem><para>Create a symlink at <arg choice="plain">DEST</arg> with target <arg choice="plain">SRC</arg></para></listitem>
</varlistentry>
<varlistentry>
<term><option>--chmod <arg choice="plain">OCTAL</arg> <arg choice="plain">PATH</arg></option></term>

View File

@@ -56,6 +56,8 @@ 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 \
@@ -79,6 +81,8 @@ if command -v yum; then
yum -y install \
'pkgconfig(libselinux)' \
/usr/bin/eu-readelf \
autoconf \
automake \
docbook-style-xsl \
gcc \
git \

View File

@@ -51,19 +51,15 @@ _bwrap() {
--hostname
--info-fd
--lock-file
--overlay
--overlay-src
--perms
--proc
--remount-ro
--ro-bind
--ro-overlay
--seccomp
--setenv
--size
--symlink
--sync-fd
--tmp-overlay
--uid
--unsetenv
--userns-block-fd

View File

@@ -13,6 +13,7 @@ if bash_completion_dir == ''
default_value: '',
pkgconfig: 'completionsdir',
pkgconfig_define: [
'prefix', get_option('prefix'),
'datadir', get_option('prefix') / get_option('datadir'),
],
)

158
configure.ac Normal file
View File

@@ -0,0 +1,158 @@
AC_PREREQ([2.63])
AC_INIT([bubblewrap], [0.8.0], [atomic-devel@projectatomic.io])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
AC_USE_SYSTEM_EXTENSIONS
AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign no-define tar-ustar no-dist-gzip dist-xz])
AM_MAINTAINER_MODE([enable])
AM_SILENT_RULES([yes])
AC_SYS_LARGEFILE
AC_PROG_CC
AM_PROG_CC_C_O
PKG_PROG_PKG_CONFIG([])
AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])])
AC_ARG_ENABLE(man,
[AS_HELP_STRING([--enable-man],
[generate man pages [default=auto]])],,
enable_man=maybe)
AS_IF([test "$enable_man" != no], [
AC_PATH_PROG([XSLTPROC], [xsltproc], [])
AS_IF([test -z "$XSLTPROC"], [
AS_IF([test "$enable_man" = yes], [
AC_MSG_ERROR([xsltproc is required for --enable-man])
])
enable_man=no
], [
enable_man=yes
])
])
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
AC_ARG_WITH([bash-completion-dir],
AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
[Install the bash auto-completion script in this directory. @<:@default=yes@:>@]),
[],
[with_bash_completion_dir=yes])
AS_IF([test "x$with_bash_completion_dir" = "xyes"],
[
PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
[BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"],
[BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
],
[
BASH_COMPLETION_DIR="$with_bash_completion_dir"
])
AC_SUBST([BASH_COMPLETION_DIR])
AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"])
AC_ARG_WITH([zsh-completion-dir],
AS_HELP_STRING([--with-zsh-completion-dir[=PATH]],
[Install the zsh auto-completion script in this directory. @<:@default=yes@:>@]),
[],
[with_zsh_completion_dir=yes])
AS_IF([test "x$with_zsh_completion_dir" = "xyes"],
[ZSH_COMPLETION_DIR="$datadir/zsh/site-functions"],
[ZSH_COMPLETION_DIR="$with_zsh_completion_dir"])
AC_SUBST([ZSH_COMPLETION_DIR])
AM_CONDITIONAL([ENABLE_ZSH_COMPLETION], [test "x$with_zsh_completion_dir" != "xno"])
# ------------------------------------------------------------------------------
have_selinux=no
AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support]))
AS_IF([test "x$enable_selinux" != "xno"], [
PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9],
[AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available])
have_selinux=yes
M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"],
[have_selinux=no])
AS_IF([test "x$have_selinux" = xno && test "x$enable_selinux" = xyes],
[AC_MSG_ERROR([*** SELinux support requested but libraries not found])])
PKG_CHECK_MODULES([SELINUX_2_3], [libselinux >= 2.3],
[AC_DEFINE(HAVE_SELINUX_2_3, 1, [Define if SELinux is version >= 2.3])],
[:])
])
AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"])
dnl Keep this in sync with ostree, except remove -Werror=declaration-after-statement
CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\
-pipe \
-Wall \
-Werror=shadow \
-Werror=empty-body \
-Werror=strict-prototypes \
-Werror=missing-prototypes \
-Werror=implicit-function-declaration \
"-Werror=format=2 -Werror=format-security -Werror=format-nonliteral" \
-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 \
-Werror=switch-default \
-Wswitch-enum \
])
AC_SUBST(WARN_CFLAGS)
AC_CHECK_LIB(cap, cap_from_text)
AS_IF([test "$ac_cv_lib_cap_cap_from_text" != "yes"],
[AC_MSG_ERROR([*** libcap requested but not found])])
AC_ARG_WITH(priv-mode,
AS_HELP_STRING([--with-priv-mode=setuid/none],
[How to set privilege-raising during make install]),
[],
[with_priv_mode="none"])
AM_CONDITIONAL(PRIV_MODE_SETUID, test "x$with_priv_mode" = "xsetuid")
AC_ARG_ENABLE(sudo,
AS_HELP_STRING([--enable-sudo],[Use sudo to set privileged mode on binaries during install (only needed if --with-priv-mode used)]),
[SUDO_BIN="sudo"], [SUDO_BIN=""])
AC_SUBST([SUDO_BIN])
AC_ARG_ENABLE(require-userns,
AS_HELP_STRING([--enable-require-userns=yes/no (default no)],
[Require user namespaces by default when installed suid]),
[],
[enable_require_userns="no"])
AS_IF([ test "x$enable_require_userns" = "xyes" ], [
AC_DEFINE(ENABLE_REQUIRE_USERNS, 1, [Define if userns should be used by default in suid mode])
])
AC_PROG_AWK
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_CONFIG_FILES([
Makefile
])
AC_OUTPUT
echo "
bubblewrap $VERSION
===================
man pages (xsltproc): $enable_man
SELinux: $have_selinux
setuid mode on make install: $with_priv_mode
require default userns: $enable_require_userns
mysteriously satisfying to pop: yes"
echo ""

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

292
m4/attributes.m4 Normal file
View File

@@ -0,0 +1,292 @@
dnl Macros to check the presence of generic (non-typed) symbols.
dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com>
dnl Copyright (c) 2006-2008 xine project
dnl Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 2, or (at your option)
dnl any later version.
dnl
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
dnl 02110-1301, USA.
dnl
dnl As a special exception, the copyright owners of the
dnl macro gives unlimited permission to copy, distribute and modify the
dnl configure scripts that are the output of Autoconf when processing the
dnl Macro. You need not follow the terms of the GNU General Public
dnl License when using or distributing such scripts, even though portions
dnl of the text of the Macro appear in them. The GNU General Public
dnl License (GPL) does govern all other use of the material that
dnl constitutes the Autoconf Macro.
dnl
dnl This special exception to the GPL applies to versions of the
dnl Autoconf Macro released by this project. When you make and
dnl distribute a modified version of the Autoconf Macro, you may extend
dnl this special exception to the GPL to apply to your modified version as
dnl well.
dnl Check if FLAG in ENV-VAR is supported by compiler and append it
dnl to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to
dnl -W* as gcc cannot test for negated warnings. If a C snippet is passed,
dnl use it, otherwise use a simple main() definition that just returns 0.
dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG], [C-SNIPPET])
AC_DEFUN([CC_CHECK_FLAG_APPEND], [
AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2],
AS_TR_SH([cc_cv_$2_$3]),
[eval "AS_TR_SH([cc_save_$2])='${$2}'"
eval "AS_TR_SH([$2])='${cc_save_$2} -Werror `echo "$3" | sed 's/^-Wno-/-W/'`'"
AC_LINK_IFELSE([AC_LANG_SOURCE(ifelse([$4], [],
[int main(void) { return 0; } ],
[$4]))],
[eval "AS_TR_SH([cc_cv_$2_$3])='yes'"],
[eval "AS_TR_SH([cc_cv_$2_$3])='no'"])
eval "AS_TR_SH([$2])='$cc_save_$2'"])
AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes],
[eval "$1='${$1} $3'"])
])
dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2], [C-SNIPPET])
AC_DEFUN([CC_CHECK_FLAGS_APPEND], [
for flag in [$3]; do
CC_CHECK_FLAG_APPEND([$1], [$2], $flag, [$4])
done
])
dnl Check if the flag is supported by linker (cacheable)
dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
AC_DEFUN([CC_CHECK_LDFLAGS], [
AC_CACHE_CHECK([if $CC supports $1 flag],
AS_TR_SH([cc_cv_ldflags_$1]),
[ac_save_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $1"
AC_LINK_IFELSE([int main() { return 1; }],
[eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
[eval "AS_TR_SH([cc_cv_ldflags_$1])="])
LDFLAGS="$ac_save_LDFLAGS"
])
AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
[$2], [$3])
])
dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for
dnl the current linker to avoid undefined references in a shared object.
AC_DEFUN([CC_NOUNDEFINED], [
dnl We check $host for which systems to enable this for.
AC_REQUIRE([AC_CANONICAL_HOST])
case $host in
dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads
dnl are requested, as different implementations are present; to avoid problems
dnl use -Wl,-z,defs only for those platform not behaving this way.
*-freebsd* | *-openbsd*) ;;
*)
dnl First of all check for the --no-undefined variant of GNU ld. This allows
dnl for a much more readable command line, so that people can understand what
dnl it does without going to look for what the heck -z defs does.
for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"])
break
done
;;
esac
AC_SUBST([LDFLAGS_NOUNDEFINED])
])
dnl Check for a -Werror flag or equivalent. -Werror is the GCC
dnl and ICC flag that tells the compiler to treat all the warnings
dnl as fatal. We usually need this option to make sure that some
dnl constructs (like attributes) are not simply ignored.
dnl
dnl Other compilers don't support -Werror per se, but they support
dnl an equivalent flag:
dnl - Sun Studio compiler supports -errwarn=%all
AC_DEFUN([CC_CHECK_WERROR], [
AC_CACHE_CHECK(
[for $CC way to treat warnings as errors],
[cc_cv_werror],
[CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror],
[CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])])
])
])
AC_DEFUN([CC_CHECK_ATTRIBUTE], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))],
AS_TR_SH([cc_cv_attribute_$1]),
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
[eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
[eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
CFLAGS="$ac_save_CFLAGS"
])
AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes],
[AC_DEFINE(
AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1,
[Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))]
)
$4],
[$5])
])
AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
CC_CHECK_ATTRIBUTE(
[constructor],,
[void __attribute__((constructor)) ctor() { int a; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
CC_CHECK_ATTRIBUTE(
[format], [format(printf, n, n)],
[void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [
CC_CHECK_ATTRIBUTE(
[format_arg], [format_arg(printf)],
[char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
CC_CHECK_ATTRIBUTE(
[visibility_$1], [visibility("$1")],
[void __attribute__((visibility("$1"))) $1_function() { }],
[$2], [$3])
])
AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
CC_CHECK_ATTRIBUTE(
[nonnull], [nonnull()],
[void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_UNUSED], [
CC_CHECK_ATTRIBUTE(
[unused], ,
[void some_function(void *foo, __attribute__((unused)) void *bar);],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [
CC_CHECK_ATTRIBUTE(
[sentinel], ,
[void some_function(void *foo, ...) __attribute__((sentinel));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [
CC_CHECK_ATTRIBUTE(
[deprecated], ,
[void some_function(void *foo, ...) __attribute__((deprecated));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_ALIAS], [
CC_CHECK_ATTRIBUTE(
[alias], [weak, alias],
[void other_function(void *foo) { }
void some_function(void *foo) __attribute__((weak, alias("other_function")));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_MALLOC], [
CC_CHECK_ATTRIBUTE(
[malloc], ,
[void * __attribute__((malloc)) my_alloc(int n);],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_PACKED], [
CC_CHECK_ATTRIBUTE(
[packed], ,
[struct astructure { char a; int b; long c; void *d; } __attribute__((packed));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_CONST], [
CC_CHECK_ATTRIBUTE(
[const], ,
[int __attribute__((const)) twopow(int n) { return 1 << n; } ],
[$1], [$2])
])
AC_DEFUN([CC_FLAG_VISIBILITY], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([if $CC supports -fvisibility=hidden],
[cc_cv_flag_visibility],
[cc_flag_visibility_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden],
cc_cv_flag_visibility='yes',
cc_cv_flag_visibility='no')
CFLAGS="$cc_flag_visibility_save_CFLAGS"])
AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
[AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
[Define this if the compiler supports the -fvisibility flag])
$1],
[$2])
])
AC_DEFUN([CC_FUNC_EXPECT], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([if compiler has __builtin_expect function],
[cc_cv_func_expect],
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
AC_COMPILE_IFELSE([AC_LANG_SOURCE(
[int some_function() {
int a = 3;
return (int)__builtin_expect(a, 3);
}])],
[cc_cv_func_expect=yes],
[cc_cv_func_expect=no])
CFLAGS="$ac_save_CFLAGS"
])
AS_IF([test "x$cc_cv_func_expect" = "xyes"],
[AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1,
[Define this if the compiler supports __builtin_expect() function])
$1],
[$2])
])
AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported],
[cc_cv_attribute_aligned],
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
for cc_attribute_align_try in 64 32 16 8 4 2; do
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int main() {
static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
return c;
}])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
done
CFLAGS="$ac_save_CFLAGS"
])
if test "x$cc_cv_attribute_aligned" != "x"; then
AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned],
[Define the highest alignment supported])
fi
])

View File

@@ -1,7 +1,7 @@
project(
'bubblewrap',
'c',
version : '0.11.0',
version : '0.8.0',
meson_version : '>=0.49.0',
default_options : [
'warning_level=2',

View File

@@ -45,7 +45,7 @@ option(
'require_userns',
type : 'boolean',
description : 'require user namespaces by default when installed setuid',
value : false,
value : 'false',
)
option(
'selinux',
@@ -57,7 +57,7 @@ option(
'tests',
type : 'boolean',
description : 'build tests',
value : true,
value : 'true',
)
option(
'zsh_completion',

View File

@@ -53,8 +53,8 @@ rtnl_send_request (int rtnl_fd,
struct sockaddr_nl dst_addr = { AF_NETLINK, 0 };
ssize_t sent;
sent = TEMP_FAILURE_RETRY (sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0,
(struct sockaddr *) &dst_addr, sizeof (dst_addr)));
sent = sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0,
(struct sockaddr *) &dst_addr, sizeof (dst_addr));
if (sent < 0)
return -1;
@@ -71,7 +71,7 @@ rtnl_read_reply (int rtnl_fd,
while (1)
{
received = TEMP_FAILURE_RETRY (recv (rtnl_fd, buffer, sizeof (buffer), 0));
received = recv (rtnl_fd, buffer, sizeof (buffer), 0);
if (received < 0)
return -1;

View File

@@ -1,18 +1,18 @@
bubblewrap release checklist
============================
* Collect release notes in `NEWS`
* Update version number in `meson.build` and release date in `NEWS`
* Collect release notes
* Update version number in `configure.ac` **and** `meson.build`
* Commit the changes
* `meson dist -C ${builddir}`
* `make distcheck`
* Do any final smoke-testing, e.g. update a package, install and test it
* `git evtag sign v$VERSION`
* Include the release notes from `NEWS` in the tag message
* 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`
* Upload the tarball that you built with `make distcheck`
* Get the `sha256sum` of the tarball and append it to the description
* `Publish release`

View File

@@ -76,21 +76,6 @@ for ALT in "" "--unshare-user-try" "--unshare-pid" "--unshare-user-try --unshare
ok "can bind a destination over a symlink"
done
# Test symlink behaviour
rm -f ./symlink
$RUN --ro-bind / / --bind "$(pwd)" "$(pwd)" --symlink /dev/null "$(pwd)/symlink" true >&2
readlink ./symlink > target.txt
assert_file_has_content target.txt /dev/null
ok "--symlink works"
$RUN --ro-bind / / --bind "$(pwd)" "$(pwd)" --symlink /dev/null "$(pwd)/symlink" true >&2
ok "--symlink is idempotent"
if $RUN --ro-bind / / --bind "$(pwd)" "$(pwd)" --symlink /dev/full "$(pwd)/symlink" true 2>err.txt; then
fatal "creating a conflicting symlink should have failed"
else
assert_file_has_content err.txt "Can't make symlink .*: existing destination is /dev/null"
fi
ok "--symlink doesn't overwrite a conflicting symlink"
# Test devices
$RUN --unshare-pid --dev /dev ls -al /dev/{stdin,stdout,stderr,null,random,urandom,fd,core} >/dev/null
ok "all expected devices were created"
@@ -109,7 +94,7 @@ assert_file_has_content json-status.json '"child-pid": [0-9]'
assert_file_has_content_literal json-status.json '"exit-code": 42'
ok "info and json-status fd"
DATA=$($RUN --proc /proc --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'stat -L -c "%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)
for NS in "ipc" "mnt" "net" "pid" "uts"; do
@@ -565,128 +550,4 @@ $RUN --argv0 right sh -c 'echo $0' > stdout
assert_file_has_content stdout right
ok "argv0 manipulation"
echo "foobar" > file-data
$RUN --proc /proc --dev /dev --bind / / --bind-fd 100 /tmp cat /tmp/file-data 100< . > stdout
assert_file_has_content stdout foobar
ok "bind-fd"
$RUN --chdir / --chdir / true > stdout 2>&1
assert_file_has_content stdout '^bwrap: Only the last --chdir option will take effect$'
ok "warning logged for redundant --chdir"
$RUN --level-prefix --chdir / --chdir / true > stdout 2>&1
assert_file_has_content stdout '^<4>bwrap: Only the last --chdir option will take effect$'
ok "--level-prefix"
if test -n "${bwrap_is_suid:-}"; then
ok_skip "no --overlay support"
ok_skip "no --overlay support"
ok_skip "no --tmp-overlay support"
ok_skip "no --ro-overlay support"
ok_skip "no --overlay-src support"
else
mkdir lower1 lower2 upper work
printf 1 > lower1/a
printf 2 > lower1/b
printf 3 > lower2/b
printf 4 > upper/a
# Check if unprivileged overlayfs is available
if ! unshare -rm mount -t overlay -o lowerdir=lower1,upperdir=upper,workdir=work,userxattr overlay lower2; then
ok_skip "no kernel support for unprivileged overlayfs"
ok_skip "no kernel support for unprivileged overlayfs"
ok_skip "no kernel support for unprivileged overlayfs"
ok_skip "no kernel support for unprivileged overlayfs"
ok_skip "no kernel support for unprivileged overlayfs"
else
# Test --overlay
if $RUN --overlay upper work /tmp true 2>err.txt; then
assert_not_reached At least one --overlay-src not required
fi
assert_file_has_content err.txt "^bwrap: --overlay requires at least one --overlay-src"
$RUN --overlay-src lower1 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/a > stdout
assert_file_has_content stdout '^4$'
$RUN --overlay-src lower1 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/b > stdout
assert_file_has_content stdout '^2$'
$RUN --overlay-src lower1 --overlay-src lower2 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/a > stdout
assert_file_has_content stdout '^4$'
$RUN --overlay-src lower1 --overlay-src lower2 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/b > stdout
assert_file_has_content stdout '^3$'
$RUN --overlay-src lower1 --overlay-src lower2 --overlay upper work /tmp/x/y/z sh -c 'printf 5 > /tmp/x/y/z/b; cat /tmp/x/y/z/b' > stdout
assert_file_has_content stdout '^5$'
assert_file_has_content upper/b '^5$'
ok "--overlay"
# Test --overlay path escaping
# Coincidentally, ":,\ is the face I make contemplating anyone who might
# need this functionality, not that that's going to stop me from supporting
# it.
mkdir 'lower ":,\' 'upper ":,\' 'work ":,\'
printf 1 > 'lower ":,\'/a
$RUN --overlay-src 'lower ":,\' --overlay 'upper ":,\' 'work ":,\' /tmp/x sh -c 'cat /tmp/x/a; printf 2 > /tmp/x/a; cat /tmp/x/a' > stdout
assert_file_has_content stdout '^12$'
assert_file_has_content 'lower ":,\'/a '^1$'
assert_file_has_content 'upper ":,\'/a '^2$'
ok "--overlay path escaping"
# Test --tmp-overlay
printf 1 > lower1/a
printf 2 > lower1/b
printf 3 > lower2/b
if $RUN --tmp-overlay /tmp true 2>err.txt; then
assert_not_reached At least one --overlay-src not required
fi
assert_file_has_content err.txt "^bwrap: --tmp-overlay requires at least one --overlay-src"
$RUN --overlay-src lower1 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/a > stdout
assert_file_has_content stdout '^1$'
$RUN --overlay-src lower1 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/b > stdout
assert_file_has_content stdout '^2$'
$RUN --overlay-src lower1 --overlay-src lower2 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/a > stdout
assert_file_has_content stdout '^1$'
$RUN --overlay-src lower1 --overlay-src lower2 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/b > stdout
assert_file_has_content stdout '^3$'
$RUN --overlay-src lower1 --overlay-src lower2 --tmp-overlay /tmp/x/y/z sh -c 'printf 4 > /tmp/x/y/z/b; cat /tmp/x/y/z/b' > stdout
assert_file_has_content stdout '^4$'
$RUN --overlay-src lower1 --tmp-overlay /tmp/x --overlay-src lower2 --tmp-overlay /tmp/y sh -c 'cat /tmp/x/b; printf 4 > /tmp/x/b; cat /tmp/x/b; cat /tmp/y/b' > stdout
assert_file_has_content stdout '^243$'
assert_file_has_content lower1/b '^2$'
assert_file_has_content lower2/b '^3$'
ok "--tmp-overlay"
# Test --ro-overlay
printf 1 > lower1/a
printf 2 > lower1/b
printf 3 > lower2/b
if $RUN --ro-overlay /tmp true 2>err.txt; then
assert_not_reached At least two --overlay-src not required
fi
assert_file_has_content err.txt "^bwrap: --ro-overlay requires at least two --overlay-src"
if $RUN --overlay-src lower1 --ro-overlay /tmp true 2>err.txt; then
assert_not_reached At least two --overlay-src not required
fi
assert_file_has_content err.txt "^bwrap: --ro-overlay requires at least two --overlay-src"
$RUN --overlay-src lower1 --overlay-src lower2 --ro-overlay /tmp/x/y/z cat /tmp/x/y/z/a > stdout
assert_file_has_content stdout '^1$'
$RUN --overlay-src lower1 --overlay-src lower2 --ro-overlay /tmp/x/y/z cat /tmp/x/y/z/b > stdout
assert_file_has_content stdout '^3$'
$RUN --overlay-src lower1 --overlay-src lower2 --ro-overlay /tmp/x/y/z sh -c 'printf 4 > /tmp/x/y/z/b; cat /tmp/x/y/z/b' > stdout
assert_file_has_content stdout '^3$'
ok "--ro-overlay"
# Test --overlay-src restrictions
if $RUN --overlay-src /tmp true 2>err.txt; then
assert_not_reached Trailing --overlay-src allowed
fi
assert_file_has_content err.txt "^bwrap: --overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay"
if $RUN --overlay-src /tmp --chdir / true 2>err.txt; then
assert_not_reached --overlay-src allowed to precede non-overlay options
fi
assert_file_has_content err.txt "^bwrap: --overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay"
ok "--overlay-src restrictions"
fi
fi
done_testing

View File

@@ -170,15 +170,15 @@ test_has_path_prefix (void)
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 },
{ "/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 },
{ "foo/bar", "/foo", TRUE },
{ "/foo/bar", "foo", TRUE },
};
size_t i;
@@ -200,37 +200,6 @@ test_has_path_prefix (void)
}
}
static void
test_string_builder (void)
{
StringBuilder sb = {0};
strappend (&sb, "aaa");
g_assert_cmpstr (sb.str, ==, "aaa");
strappend (&sb, "bbb");
g_assert_cmpstr (sb.str, ==, "aaabbb");
strappendf (&sb, "c%dc%s", 9, "x");
g_assert_cmpstr (sb.str, ==, "aaabbbc9cx");
strappend_escape_for_mount_options (&sb, "/path :,\\");
g_assert_cmpstr (sb.str, ==, "aaabbbc9cx/path \\:\\,\\\\");
strappend (&sb, "zzz");
g_assert_cmpstr (sb.str, ==, "aaabbbc9cx/path \\:\\,\\\\zzz");
free (sb.str);
sb = (StringBuilder){0};
strappend_escape_for_mount_options (&sb, "aaa");
g_assert_cmpstr (sb.str, ==, "aaa");
free (sb.str);
sb = (StringBuilder){0};
strappend_escape_for_mount_options (&sb, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
g_assert_cmpstr (sb.str, ==, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
free (sb.str);
}
int
main (int argc UNUSED,
char **argv UNUSED)
@@ -241,7 +210,6 @@ main (int argc UNUSED,
test_strconcat3 ();
test_has_prefix ();
test_has_path_prefix ();
test_string_builder ();
printf ("1..%u\n", test_number);
return 0;
}

271
utils.c
View File

@@ -19,11 +19,8 @@
#include "config.h"
#include "utils.h"
#include <limits.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/param.h>
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
@@ -35,34 +32,21 @@
#define security_check_context(x) security_check_context ((security_context_t) x)
#endif
bool bwrap_level_prefix = false;
__attribute__((format(printf, 2, 0))) static void
bwrap_logv (int severity,
const char *format,
va_list args,
const char *detail)
__attribute__((format(printf, 1, 0))) static void
warnv (const char *format, va_list args)
{
if (bwrap_level_prefix)
fprintf (stderr, "<%d>", severity);
fprintf (stderr, "bwrap: ");
vfprintf (stderr, format, args);
if (detail != NULL)
fprintf (stderr, ": %s", detail);
fprintf (stderr, "\n");
}
void
bwrap_log (int severity,
const char *format, ...)
warn (const char *format, ...)
{
va_list args;
va_start (args, format);
bwrap_logv (severity, format, args, NULL);
warnv (format, args);
va_end (args);
}
@@ -74,25 +58,14 @@ die_with_error (const char *format, ...)
errsv = errno;
va_start (args, format);
bwrap_logv (LOG_ERR, format, args, strerror (errsv));
va_end (args);
exit (1);
}
void
die_with_mount_error (const char *format, ...)
{
va_list args;
int errsv;
errsv = errno;
fprintf (stderr, "bwrap: ");
va_start (args, format);
bwrap_logv (LOG_ERR, format, args, mount_strerror (errsv));
vfprintf (stderr, format, args);
va_end (args);
fprintf (stderr, ": %s\n", strerror (errsv));
exit (1);
}
@@ -102,7 +75,7 @@ die (const char *format, ...)
va_list args;
va_start (args, format);
bwrap_logv (LOG_ERR, format, args, NULL);
warnv (format, args);
va_end (args);
exit (1);
@@ -153,9 +126,9 @@ xmalloc (size_t size)
}
void *
xcalloc (size_t nmemb, size_t size)
xcalloc (size_t size)
{
void *res = calloc (nmemb, size);
void *res = calloc (1, size);
if (res == NULL)
die_oom ();
@@ -165,13 +138,9 @@ xcalloc (size_t nmemb, size_t size)
void *
xrealloc (void *ptr, size_t size)
{
void *res;
void *res = realloc (ptr, size);
assert (size != 0);
res = realloc (ptr, size);
if (res == NULL)
if (size != 0 && res == NULL)
die_oom ();
return res;
}
@@ -214,7 +183,7 @@ bool
has_path_prefix (const char *str,
const char *prefix)
{
while (true)
while (TRUE)
{
/* Skip consecutive slashes to reach next path
element */
@@ -225,13 +194,13 @@ has_path_prefix (const char *str,
/* No more prefix path elements? Done! */
if (*prefix == 0)
return true;
return TRUE;
/* Compare path element */
while (*prefix != 0 && *prefix != '/')
{
if (*str != *prefix)
return false;
return FALSE;
str++;
prefix++;
}
@@ -239,7 +208,7 @@ has_path_prefix (const char *str,
/* Matched prefix path element,
must be entire str path element */
if (*str != '/' && *str != 0)
return false;
return FALSE;
}
}
@@ -247,7 +216,7 @@ bool
path_equal (const char *path1,
const char *path2)
{
while (true)
while (TRUE)
{
/* Skip consecutive slashes to reach next path
element */
@@ -264,14 +233,14 @@ path_equal (const char *path1,
while (*path1 != 0 && *path1 != '/')
{
if (*path1 != *path2)
return false;
return FALSE;
path1++;
path2++;
}
/* Matched path1 path element, must be entire path element */
if (*path2 != '/' && *path2 != 0)
return false;
return FALSE;
}
}
@@ -378,7 +347,7 @@ fdwalk (int proc_fd, int (*cb)(void *data,
int res = 0;
DIR *d;
dfd = TEMP_FAILURE_RETRY (openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY));
dfd = openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
if (dfd == -1)
return res;
@@ -452,7 +421,7 @@ write_to_fd (int fd,
/* Sets errno on error (!= 0), ENOSPC on short write */
int
write_file_at (int dfd,
write_file_at (int dirfd,
const char *path,
const char *content)
{
@@ -460,7 +429,7 @@ write_file_at (int dfd,
bool res;
int errsv;
fd = TEMP_FAILURE_RETRY (openat (dfd, path, O_RDWR | O_CLOEXEC, 0));
fd = openat (dirfd, path, O_RDWR | O_CLOEXEC, 0);
if (fd == -1)
return -1;
@@ -485,7 +454,7 @@ create_file (const char *path,
int res;
int errsv;
fd = TEMP_FAILURE_RETRY (creat (path, mode));
fd = creat (path, mode);
if (fd == -1)
return -1;
@@ -534,7 +503,7 @@ copy_file_data (int sfd,
char buffer[BUFSIZE];
ssize_t bytes_read;
while (true)
while (TRUE)
{
bytes_read = read (sfd, buffer, BUFSIZE);
if (bytes_read == -1)
@@ -566,11 +535,11 @@ copy_file (const char *src_path,
int res;
int errsv;
sfd = TEMP_FAILURE_RETRY (open (src_path, O_CLOEXEC | O_RDONLY));
sfd = open (src_path, O_CLOEXEC | O_RDONLY);
if (sfd == -1)
return -1;
dfd = TEMP_FAILURE_RETRY (creat (dst_path, mode));
dfd = creat (dst_path, mode);
if (dfd == -1)
{
errsv = errno;
@@ -608,12 +577,6 @@ load_file_data (int fd,
{
if (data_len == data_read + 1)
{
if (data_len > SSIZE_MAX / 2)
{
errno = EFBIG;
return NULL;
}
data_len *= 2;
data = xrealloc (data, data_len);
}
@@ -640,14 +603,14 @@ load_file_data (int fd,
/* Sets errno on error (== NULL),
* Always ensures terminating zero */
char *
load_file_at (int dfd,
load_file_at (int dirfd,
const char *path)
{
int fd;
char *data;
int errsv;
fd = TEMP_FAILURE_RETRY (openat (dfd, path, O_CLOEXEC | O_RDONLY));
fd = openat (dirfd, path, O_CLOEXEC | O_RDONLY);
if (fd == -1)
return NULL;
@@ -753,15 +716,15 @@ mkdir_with_parents (const char *pathname,
read back with read_pid_from_socket(), and then the kernel has
translated it between namespaces as needed. */
void
send_pid_on_socket (int sockfd)
send_pid_on_socket (int socket)
{
char buf[1] = { 0 };
struct msghdr msg = {};
struct iovec iov = { buf, sizeof (buf) };
const ssize_t control_len_snd = CMSG_SPACE(sizeof(struct ucred));
_Alignas(struct cmsghdr) char control_buf_snd[control_len_snd];
char control_buf_snd[control_len_snd];
struct cmsghdr *cmsg;
struct ucred cred;
struct ucred *cred;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
@@ -772,13 +735,13 @@ send_pid_on_socket (int sockfd)
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDENTIALS;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
cred = (struct ucred *)CMSG_DATA(cmsg);
cred.pid = getpid ();
cred.uid = geteuid ();
cred.gid = getegid ();
memcpy (CMSG_DATA (cmsg), &cred, sizeof (cred));
cred->pid = getpid ();
cred->uid = geteuid ();
cred->gid = getegid ();
if (TEMP_FAILURE_RETRY (sendmsg (sockfd, &msg, 0)) < 0)
if (sendmsg (socket, &msg, 0) < 0)
die_with_error ("Can't send pid");
}
@@ -795,13 +758,13 @@ create_pid_socketpair (int sockets[2])
}
int
read_pid_from_socket (int sockfd)
read_pid_from_socket (int socket)
{
char recv_buf[1] = { 0 };
struct msghdr msg = {};
struct iovec iov = { recv_buf, sizeof (recv_buf) };
const ssize_t control_len_rcv = CMSG_SPACE(sizeof(struct ucred));
_Alignas(struct cmsghdr) char control_buf_rcv[control_len_rcv];
char control_buf_rcv[control_len_rcv];
struct cmsghdr* cmsg;
msg.msg_iov = &iov;
@@ -809,7 +772,7 @@ read_pid_from_socket (int sockfd)
msg.msg_control = control_buf_rcv;
msg.msg_controllen = control_len_rcv;
if (TEMP_FAILURE_RETRY (recvmsg (sockfd, &msg, 0)) < 0)
if (recvmsg (socket, &msg, 0) < 0)
die_with_error ("Can't read pid from socket");
if (msg.msg_controllen <= 0)
@@ -822,10 +785,8 @@ read_pid_from_socket (int sockfd)
cmsg->cmsg_type == SCM_CREDENTIALS &&
payload_len == sizeof(struct ucred))
{
struct ucred cred;
memcpy (&cred, CMSG_DATA (cmsg), sizeof (cred));
return cred.pid;
struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
return cred->pid;
}
}
die ("No pid returned on socket");
@@ -842,8 +803,6 @@ readlink_malloc (const char *pathname)
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);
@@ -932,149 +891,3 @@ label_exec (UNUSED const char *exec_label)
#endif
return 0;
}
/*
* Like strerror(), but specialized for a failed mount(2) call.
*/
const char *
mount_strerror (int errsv)
{
switch (errsv)
{
case ENOSPC:
/* "No space left on device" misleads users into thinking there
* is some sort of disk-space problem, but mount(2) uses that
* errno value to mean something more like "limit exceeded". */
return ("Limit exceeded (ENOSPC). "
"(Hint: Check that /proc/sys/fs/mount-max is sufficient, "
"typically 100000)");
default:
return strerror (errsv);
}
}
/*
* Return a + b if it would not overflow.
* Die with an "out of memory" error if it would.
*/
static size_t
xadd (size_t a, size_t b)
{
#if defined(__GNUC__) && __GNUC__ >= 5
size_t result;
if (__builtin_add_overflow (a, b, &result))
die_oom ();
return result;
#else
if (a > SIZE_MAX - b)
die_oom ();
return a + b;
#endif
}
/*
* Return a * b if it would not overflow.
* Die with an "out of memory" error if it would.
*/
static size_t
xmul (size_t a, size_t b)
{
#if defined(__GNUC__) && __GNUC__ >= 5
size_t result;
if (__builtin_mul_overflow (a, b, &result))
die_oom ();
return result;
#else
if (b != 0 && a > SIZE_MAX / b)
die_oom ();
return a * b;
#endif
}
void
strappend (StringBuilder *dest, const char *src)
{
size_t len = strlen (src);
size_t new_offset = xadd (dest->offset, len);
if (new_offset >= dest->size)
{
dest->size = xmul (xadd (new_offset, 1), 2);
dest->str = xrealloc (dest->str, dest->size);
}
/* Preserves the invariant that dest->str is always null-terminated, even
* though the offset is positioned at the null byte for the next write.
*/
strncpy (dest->str + dest->offset, src, len + 1);
dest->offset = new_offset;
}
__attribute__((format (printf, 2, 3)))
void
strappendf (StringBuilder *dest, const char *fmt, ...)
{
va_list args;
int len;
size_t new_offset;
va_start (args, fmt);
len = vsnprintf (dest->str + dest->offset, dest->size - dest->offset, fmt, args);
va_end (args);
if (len < 0)
die_with_error ("vsnprintf");
new_offset = xadd (dest->offset, len);
if (new_offset >= dest->size)
{
dest->size = xmul (xadd (new_offset, 1), 2);
dest->str = xrealloc (dest->str, dest->size);
va_start (args, fmt);
len = vsnprintf (dest->str + dest->offset, dest->size - dest->offset, fmt, args);
va_end (args);
if (len < 0)
die_with_error ("vsnprintf");
}
dest->offset = new_offset;
}
void
strappend_escape_for_mount_options (StringBuilder *dest, const char *src)
{
bool unescaped = true;
for (;;)
{
if (dest->offset == dest->size)
{
dest->size = MAX (64, xmul (dest->size, 2));
dest->str = xrealloc (dest->str, dest->size);
}
switch (*src)
{
case '\0':
dest->str[dest->offset] = '\0';
return;
case '\\':
case ',':
case ':':
if (unescaped)
{
dest->str[dest->offset++] = '\\';
unescaped = false;
continue;
}
/* else fall through */
default:
dest->str[dest->offset++] = *src;
unescaped = true;
break;
}
src++;
}
}

51
utils.h
View File

@@ -24,33 +24,26 @@
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#if 0
#define debug(...) bwrap_log (LOG_DEBUG, __VA_ARGS__)
#if 1
#define __debug__(x) printf x
#else
#define debug(...)
#define __debug__(x)
#endif
#define UNUSED __attribute__((__unused__))
#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
#ifndef TEMP_FAILURE_RETRY
#define TEMP_FAILURE_RETRY(expression) \
(__extension__ \
({ long int __result; \
do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \
__result; }))
#endif
#define TRUE 1
#define FALSE 0
typedef int bool;
#define PIPE_READ_END 0
#define PIPE_WRITE_END 1
@@ -59,17 +52,10 @@
#define PR_SET_CHILD_SUBREAPER 36
#endif
extern bool bwrap_level_prefix;
void bwrap_log (int severity,
const char *format,
...) __attribute__((format (printf, 2, 3)));
#define warn(...) bwrap_log (LOG_WARNING, __VA_ARGS__)
void warn (const char *format,
...) __attribute__((format (printf, 1, 2)));
void die_with_error (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
void die_with_mount_error (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
void die (const char *format,
...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
void die_oom (void) __attribute__((__noreturn__));
@@ -78,7 +64,7 @@ void die_unless_label_valid (const char *label);
void fork_intermediate_child (void);
void *xmalloc (size_t size);
void *xcalloc (size_t nmemb, size_t size);
void *xcalloc (size_t size);
void *xrealloc (void *ptr,
size_t size);
char *xstrdup (const char *str);
@@ -148,8 +134,6 @@ char *label_mount (const char *opt,
int label_exec (const char *exec_label);
int label_create_file (const char *file_label);
const char *mount_strerror (int errsv);
static inline void
cleanup_freep (void *p)
{
@@ -198,20 +182,3 @@ steal_pointer (void *pp)
/* type safety */
#define steal_pointer(pp) \
(0 ? (*(pp)) : (steal_pointer) (pp))
typedef struct _StringBuilder StringBuilder;
struct _StringBuilder
{
char * str;
size_t size;
size_t offset;
};
void strappend (StringBuilder *dest,
const char *src);
void strappendf (StringBuilder *dest,
const char *fmt,
...);
void strappend_escape_for_mount_options (StringBuilder *dest,
const char *src);