Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9ca3b05ec7 | ||
![]() |
73264b77d2 | ||
![]() |
833b32d834 | ||
![]() |
4021ac2936 | ||
![]() |
3e11554a0d | ||
![]() |
0545e72383 | ||
![]() |
1fd7c383ef | ||
![]() |
33923454fd | ||
![]() |
759517f892 | ||
![]() |
bcd9614380 | ||
![]() |
c1bfc72043 | ||
![]() |
ff33964f33 | ||
![]() |
f371022ad6 | ||
![]() |
50cdea6788 | ||
![]() |
812cc7f621 | ||
![]() |
47ff41eb90 | ||
![]() |
73abd50683 | ||
![]() |
5dab8b8916 | ||
![]() |
0c9646573f | ||
![]() |
654a25d408 | ||
![]() |
2a552429ec | ||
![]() |
2cca54f7c6 | ||
![]() |
0f99752f6c | ||
![]() |
2d382867c9 | ||
![]() |
c17b4a8709 | ||
![]() |
67400e1af6 | ||
![]() |
fc1dd3dd37 | ||
![]() |
11b81bbf10 | ||
![]() |
d2f85e51a4 | ||
![]() |
2834c01cab | ||
![]() |
89ae6b10a4 | ||
![]() |
9b9fa15a72 | ||
![]() |
2514597193 | ||
![]() |
dc63ec667e | ||
![]() |
8b7a174930 | ||
![]() |
aeeade8ef6 | ||
![]() |
a253257cd2 | ||
![]() |
973fe36146 | ||
![]() |
83af951948 | ||
![]() |
8e51677abd | ||
![]() |
041e3c5085 | ||
![]() |
b6bbba5ceb | ||
![]() |
8653799bb6 | ||
![]() |
f4c7e48870 | ||
![]() |
7fe0996b77 | ||
![]() |
65a19f273f | ||
![]() |
8789aa42e4 | ||
![]() |
454bfbf9da | ||
![]() |
82d6197e90 | ||
![]() |
526d6ebd74 | ||
![]() |
623780a35b | ||
![]() |
84dc66c5ee | ||
![]() |
422c078ed8 | ||
![]() |
9aa1b3636b | ||
![]() |
5bc4c68006 | ||
![]() |
913f92be36 | ||
![]() |
51a818858c | ||
![]() |
4109d59251 | ||
![]() |
4872aefb1d | ||
![]() |
68a0330ce4 | ||
![]() |
4351ec20e8 | ||
![]() |
4518233eac |
78
.github/workflows/check.yml
vendored
78
.github/workflows/check.yml
vendored
@@ -9,72 +9,12 @@ 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@v1
|
||||
uses: actions/checkout@v4
|
||||
- name: Install build-dependencies
|
||||
run: sudo ./ci/builddeps.sh
|
||||
- name: Create logs dir
|
||||
@@ -129,7 +69,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@v1
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() || cancelled()
|
||||
with:
|
||||
name: test logs
|
||||
@@ -145,23 +85,19 @@ jobs:
|
||||
- cpp
|
||||
steps:
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Check out
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
- name: Install build-dependencies
|
||||
run: sudo ./ci/builddeps.sh --clang
|
||||
- name: autogen.sh
|
||||
run: NOCONFIGURE=1 ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --enable-selinux
|
||||
- run: meson build -Dselinux=enabled
|
||||
env:
|
||||
CC: clang
|
||||
CFLAGS: >-
|
||||
-O2
|
||||
-Werror=unused-variable
|
||||
- name: make
|
||||
run: make -j $(getconf _NPROCESSORS_ONLN) V=1
|
||||
- run: meson compile -C build
|
||||
- name: CodeQL analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
@@ -1,12 +0,0 @@
|
||||
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)
|
@@ -1,18 +0,0 @@
|
||||
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
102
Makefile.am
@@ -1,102 +0,0 @@
|
||||
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
Normal file
51
NEWS.md
Normal file
@@ -0,0 +1,51 @@
|
||||
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>
|
55
README.md
55
README.md
@@ -68,13 +68,16 @@ 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](http://www.flatpak.org)
|
||||
- [Flatpak](https://www.flatpak.org)
|
||||
- [rpm-ostree unprivileged](https://github.com/projectatomic/rpm-ostree/pull/209)
|
||||
- [bwrap-oci](https://github.com/projectatomic/bwrap-oci)
|
||||
|
||||
@@ -89,25 +92,15 @@ 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.
|
||||
If you need to build bubblewrap from source, you can do this with meson:
|
||||
|
||||
meson:
|
||||
|
||||
```
|
||||
```sh
|
||||
meson _builddir
|
||||
meson compile -C _builddir
|
||||
meson test -C _builddir
|
||||
meson install -C _builddir
|
||||
```
|
||||
|
||||
autotools:
|
||||
|
||||
```
|
||||
./autogen.sh
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
@@ -152,26 +145,50 @@ 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](http://linux.die.net/man/2/clone)): This hides all but the current uid and gid from the
|
||||
User namespaces ([CLONE_NEWUSER](https://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](http://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the
|
||||
IPC namespaces ([CLONE_NEWIPC](https://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](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/).
|
||||
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/).
|
||||
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
UTS namespace ([CLONE_NEWUTS](http://linux.die.net/man/2/clone)): The sandbox will have its own hostname.
|
||||
UTS namespace ([CLONE_NEWUTS](https://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).
|
||||
|
||||
If you are not filtering out `TIOCSTI` commands using seccomp filters,
|
||||
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
|
||||
------------------------------------
|
||||
|
||||
|
19
autogen.sh
19
autogen.sh
@@ -1,19 +0,0 @@
|
||||
#!/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" "$@"
|
52
bind-mount.c
52
bind-mount.c
@@ -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 (sizeof (MountInfo) * (1));
|
||||
mount_tab = xcalloc (1, sizeof (MountInfo));
|
||||
return steal_pointer (&mount_tab);
|
||||
}
|
||||
|
||||
by_id = xcalloc ((max_id + 1) * sizeof (MountInfoLine*));
|
||||
by_id = xcalloc (max_id + 1, sizeof (MountInfoLine*));
|
||||
for (i = 0; i < n_lines; i++)
|
||||
by_id[lines[i].id] = &lines[i];
|
||||
|
||||
@@ -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 (sizeof (MountInfo) * (n_mounts + 1));
|
||||
mount_tab = xcalloc (n_mounts + 1, sizeof (MountInfo));
|
||||
|
||||
end_tab = collect_mounts (&mount_tab[0], &lines[root]);
|
||||
assert (end_tab == &mount_tab[n_mounts]);
|
||||
@@ -405,7 +405,7 @@ bind_mount (int proc_fd,
|
||||
if (resolved_dest == NULL)
|
||||
return BIND_MOUNT_ERROR_REALPATH_DEST;
|
||||
|
||||
dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC);
|
||||
dest_fd = TEMP_FAILURE_RETRY (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,9 +557,12 @@ 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);
|
||||
@@ -571,7 +574,24 @@ die_with_bind_result (bind_mount_result res,
|
||||
/* message is leaked, but we're exiting unsuccessfully anyway, so ignore */
|
||||
|
||||
if (want_errno)
|
||||
fprintf (stderr, ": %s", strerror (saved_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);
|
||||
|
468
bubblewrap.c
468
bubblewrap.c
@@ -44,15 +44,6 @@
|
||||
#define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
/* We limit the size of a tmpfs to half the architecture's address space,
|
||||
* to avoid hitting arbitrary limits in the kernel.
|
||||
* For example, on at least one x86_64 machine, the actual limit seems to be
|
||||
@@ -74,19 +65,19 @@ static bool opt_as_pid_1;
|
||||
|
||||
static const char *opt_argv0 = NULL;
|
||||
static const char *opt_chdir_path = NULL;
|
||||
static bool opt_assert_userns_disabled = FALSE;
|
||||
static bool opt_disable_userns = FALSE;
|
||||
static bool opt_unshare_user = FALSE;
|
||||
static bool opt_unshare_user_try = FALSE;
|
||||
static bool opt_unshare_pid = FALSE;
|
||||
static bool opt_unshare_ipc = FALSE;
|
||||
static bool opt_unshare_net = FALSE;
|
||||
static bool opt_unshare_uts = FALSE;
|
||||
static bool opt_unshare_cgroup = FALSE;
|
||||
static bool opt_unshare_cgroup_try = FALSE;
|
||||
static bool opt_needs_devpts = FALSE;
|
||||
static bool opt_new_session = FALSE;
|
||||
static bool opt_die_with_parent = FALSE;
|
||||
static bool opt_assert_userns_disabled = false;
|
||||
static bool opt_disable_userns = false;
|
||||
static bool opt_unshare_user = false;
|
||||
static bool opt_unshare_user_try = false;
|
||||
static bool opt_unshare_pid = false;
|
||||
static bool opt_unshare_ipc = false;
|
||||
static bool opt_unshare_net = false;
|
||||
static bool opt_unshare_uts = false;
|
||||
static bool opt_unshare_cgroup = false;
|
||||
static bool opt_unshare_cgroup_try = false;
|
||||
static bool opt_needs_devpts = false;
|
||||
static bool opt_new_session = false;
|
||||
static bool opt_die_with_parent = false;
|
||||
static uid_t opt_sandbox_uid = -1;
|
||||
static gid_t opt_sandbox_gid = -1;
|
||||
static int opt_sync_fd = -1;
|
||||
@@ -100,8 +91,10 @@ static char *opt_args_data = NULL; /* owned */
|
||||
static int opt_userns_fd = -1;
|
||||
static int opt_userns2_fd = -1;
|
||||
static int opt_pidns_fd = -1;
|
||||
static int opt_tmp_overlay_count = 0;
|
||||
static int next_perms = -1;
|
||||
static size_t next_size_arg = 0;
|
||||
static int next_overlay_src_count = 0;
|
||||
|
||||
#define CAP_TO_MASK_0(x) (1L << ((x) & 31))
|
||||
#define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32)
|
||||
@@ -131,6 +124,10 @@ typedef enum {
|
||||
SETUP_BIND_MOUNT,
|
||||
SETUP_RO_BIND_MOUNT,
|
||||
SETUP_DEV_BIND_MOUNT,
|
||||
SETUP_OVERLAY_MOUNT,
|
||||
SETUP_TMP_OVERLAY_MOUNT,
|
||||
SETUP_RO_OVERLAY_MOUNT,
|
||||
SETUP_OVERLAY_SRC,
|
||||
SETUP_MOUNT_PROC,
|
||||
SETUP_MOUNT_DEV,
|
||||
SETUP_MOUNT_TMPFS,
|
||||
@@ -147,7 +144,7 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
NO_CREATE_DEST = (1 << 0),
|
||||
ALLOW_NOTEXIST = (2 << 0),
|
||||
ALLOW_NOTEXIST = (1 << 1),
|
||||
} SetupOpFlag;
|
||||
|
||||
typedef struct _SetupOp SetupOp;
|
||||
@@ -176,6 +173,7 @@ struct _LockFile
|
||||
enum {
|
||||
PRIV_SEP_OP_DONE,
|
||||
PRIV_SEP_OP_BIND_MOUNT,
|
||||
PRIV_SEP_OP_OVERLAY_MOUNT,
|
||||
PRIV_SEP_OP_PROC_MOUNT,
|
||||
PRIV_SEP_OP_TMPFS_MOUNT,
|
||||
PRIV_SEP_OP_DEVPTS_MOUNT,
|
||||
@@ -213,7 +211,7 @@ static Type *last_ ## name = NULL; \
|
||||
static inline Type * \
|
||||
_ ## name ## _append_new (void) \
|
||||
{ \
|
||||
Type *self = xcalloc (sizeof (Type)); \
|
||||
Type *self = xcalloc (1, sizeof (Type)); \
|
||||
\
|
||||
if (last_ ## name != NULL) \
|
||||
last_ ## name ->next = self; \
|
||||
@@ -311,6 +309,7 @@ usage (int ecode, FILE *out)
|
||||
" --version Print version\n"
|
||||
" --args FD Parse NUL-separated args from FD\n"
|
||||
" --argv0 VALUE Set argv[0] to the value VALUE before running the program\n"
|
||||
" --level-prefix Prepend e.g. <3> to diagnostic messages\n"
|
||||
" --unshare-all Unshare every namespace we support by default\n"
|
||||
" --share-net Retain the network namespace (can only combine with --unshare-all)\n"
|
||||
" --unshare-user Create new user namespace (may be automatically implied if not setuid)\n"
|
||||
@@ -341,7 +340,14 @@ usage (int ecode, FILE *out)
|
||||
" --dev-bind-try SRC DEST Equal to --dev-bind but ignores non-existent SRC\n"
|
||||
" --ro-bind SRC DEST Bind mount the host path SRC readonly on DEST\n"
|
||||
" --ro-bind-try SRC DEST Equal to --ro-bind but ignores non-existent SRC\n"
|
||||
" --bind-fd FD DEST Bind open directory or path fd on DEST\n"
|
||||
" --ro-bind-fd FD DEST Bind open directory or path fd read-only on DEST\n"
|
||||
" --remount-ro DEST Remount DEST as readonly; does not recursively remount\n"
|
||||
" --overlay-src SRC Read files from SRC in the following overlay\n"
|
||||
" --overlay RWSRC WORKDIR DEST Mount overlayfs on DEST, with RWSRC as the host path for writes and\n"
|
||||
" WORKDIR an empty directory on the same filesystem as RWSRC\n"
|
||||
" --tmp-overlay DEST Mount overlayfs on DEST, with writes going to an invisible tmpfs\n"
|
||||
" --ro-overlay DEST Mount overlayfs read-only on DEST\n"
|
||||
" --exec-label LABEL Exec label for the sandbox\n"
|
||||
" --file-label LABEL File label for temporary sandbox content\n"
|
||||
" --proc DEST Mount new procfs on DEST\n"
|
||||
@@ -474,7 +480,7 @@ report_child_exit_status (int exitc, int setup_finished_fd)
|
||||
return;
|
||||
|
||||
output = xasprintf ("{ \"exit-code\": %i }\n", exitc);
|
||||
dump_info (opt_json_status_fd, output, FALSE);
|
||||
dump_info (opt_json_status_fd, output, false);
|
||||
close (opt_json_status_fd);
|
||||
opt_json_status_fd = -1;
|
||||
close (setup_finished_fd);
|
||||
@@ -596,7 +602,7 @@ do_init (int event_fd, pid_t initial_pid)
|
||||
|
||||
for (lock = lock_files; lock != NULL; lock = lock->next)
|
||||
{
|
||||
int fd = open (lock->path, O_RDONLY | O_CLOEXEC);
|
||||
int fd = TEMP_FAILURE_RETRY (open (lock->path, O_RDONLY | O_CLOEXEC));
|
||||
if (fd == -1)
|
||||
die_with_error ("Unable to open lock file %s", lock->path);
|
||||
|
||||
@@ -607,7 +613,7 @@ do_init (int event_fd, pid_t initial_pid)
|
||||
.l_len = 0
|
||||
};
|
||||
|
||||
if (fcntl (fd, F_SETLK, &l) < 0)
|
||||
if (TEMP_FAILURE_RETRY (fcntl (fd, F_SETLK, &l)) < 0)
|
||||
die_with_error ("Unable to lock file %s", lock->path);
|
||||
|
||||
/* Keep fd open to hang on to lock */
|
||||
@@ -619,12 +625,12 @@ do_init (int event_fd, pid_t initial_pid)
|
||||
|
||||
seccomp_programs_apply ();
|
||||
|
||||
while (TRUE)
|
||||
while (true)
|
||||
{
|
||||
pid_t child;
|
||||
int status;
|
||||
|
||||
child = wait (&status);
|
||||
child = TEMP_FAILURE_RETRY (wait (&status));
|
||||
if (child == initial_pid)
|
||||
{
|
||||
initial_exit_status = propagate_exit_status (status);
|
||||
@@ -635,7 +641,7 @@ do_init (int event_fd, pid_t initial_pid)
|
||||
int res UNUSED;
|
||||
|
||||
val = initial_exit_status + 1;
|
||||
res = write (event_fd, &val, 8);
|
||||
res = TEMP_FAILURE_RETRY (write (event_fd, &val, 8));
|
||||
/* Ignore res, if e.g. the parent died and closed event_fd
|
||||
we don't want to error out here */
|
||||
}
|
||||
@@ -763,16 +769,16 @@ prctl_caps (uint32_t *caps, bool do_cap_bounding, bool do_set_ambient)
|
||||
*/
|
||||
for (cap = 0; cap <= CAP_LAST_CAP; cap++)
|
||||
{
|
||||
bool keep = FALSE;
|
||||
bool keep = false;
|
||||
if (cap < 32)
|
||||
{
|
||||
if (CAP_TO_MASK_0 (cap) & caps[0])
|
||||
keep = TRUE;
|
||||
keep = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CAP_TO_MASK_1 (cap) & caps[1])
|
||||
keep = TRUE;
|
||||
keep = true;
|
||||
}
|
||||
|
||||
if (keep && do_set_ambient)
|
||||
@@ -801,11 +807,11 @@ static void
|
||||
drop_cap_bounding_set (bool drop_all)
|
||||
{
|
||||
if (!drop_all)
|
||||
prctl_caps (requested_caps, TRUE, FALSE);
|
||||
prctl_caps (requested_caps, true, false);
|
||||
else
|
||||
{
|
||||
uint32_t no_caps[2] = {0, 0};
|
||||
prctl_caps (no_caps, TRUE, FALSE);
|
||||
prctl_caps (no_caps, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,13 +820,13 @@ set_ambient_capabilities (void)
|
||||
{
|
||||
if (is_privileged)
|
||||
return;
|
||||
prctl_caps (requested_caps, FALSE, TRUE);
|
||||
prctl_caps (requested_caps, false, true);
|
||||
}
|
||||
|
||||
/* This acquires the privileges that the bwrap will need it to work.
|
||||
* If bwrap is not setuid, then this does nothing, and it relies on
|
||||
* unprivileged user namespaces to be used. This case is
|
||||
* "is_privileged = FALSE".
|
||||
* "is_privileged = false".
|
||||
*
|
||||
* If bwrap is setuid, then we do things in phases.
|
||||
* The first part is run as euid 0, but with fsuid as the real user.
|
||||
@@ -844,7 +850,7 @@ acquire_privs (void)
|
||||
if (euid != 0)
|
||||
die ("Unexpected setuid user %d, should be 0", euid);
|
||||
|
||||
is_privileged = TRUE;
|
||||
is_privileged = true;
|
||||
/* We want to keep running as euid=0 until at the clone()
|
||||
* operation because doing so will make the user namespace be
|
||||
* owned by root, which makes it not ptrace:able by the user as
|
||||
@@ -865,7 +871,7 @@ acquire_privs (void)
|
||||
die ("Unable to set fsuid (was %d)", (int)new_fsuid);
|
||||
|
||||
/* We never need capabilities after execve(), so lets drop everything from the bounding set */
|
||||
drop_cap_bounding_set (TRUE);
|
||||
drop_cap_bounding_set (true);
|
||||
|
||||
/* Keep only the required capabilities for setup */
|
||||
set_required_caps ();
|
||||
@@ -902,7 +908,7 @@ switch_to_user_with_privs (void)
|
||||
{
|
||||
/* If we're in a new user namespace, we got back the bounding set, clear it again */
|
||||
if (opt_unshare_user || opt_userns_fd != -1)
|
||||
drop_cap_bounding_set (FALSE);
|
||||
drop_cap_bounding_set (false);
|
||||
|
||||
/* If we switched to a new user namespace it may allow other uids/gids, so switch to the target one */
|
||||
if (opt_userns_fd != -1)
|
||||
@@ -1059,10 +1065,10 @@ privileged_op (int privileged_op_socket,
|
||||
if (arg2 != NULL)
|
||||
strcpy ((char *) buffer + arg2_offset, arg2);
|
||||
|
||||
if (write (privileged_op_socket, buffer, buffer_size) != (ssize_t)buffer_size)
|
||||
if (TEMP_FAILURE_RETRY (write (privileged_op_socket, buffer, buffer_size)) != (ssize_t)buffer_size)
|
||||
die ("Can't write to privileged_op_socket");
|
||||
|
||||
if (read (privileged_op_socket, buffer, 1) != 1)
|
||||
if (TEMP_FAILURE_RETRY (read (privileged_op_socket, buffer, 1)) != 1)
|
||||
die ("Can't read from privileged_op_socket");
|
||||
|
||||
return;
|
||||
@@ -1113,7 +1119,7 @@ privileged_op (int privileged_op_socket,
|
||||
|
||||
case PRIV_SEP_OP_PROC_MOUNT:
|
||||
if (mount ("proc", arg1, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0)
|
||||
die_with_error ("Can't mount proc on %s", arg1);
|
||||
die_with_mount_error ("Can't mount proc on %s", arg1);
|
||||
break;
|
||||
|
||||
case PRIV_SEP_OP_TMPFS_MOUNT:
|
||||
@@ -1132,19 +1138,33 @@ privileged_op (int privileged_op_socket,
|
||||
|
||||
cleanup_free char *opt = label_mount (mode, opt_file_label);
|
||||
if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0)
|
||||
die_with_error ("Can't mount tmpfs on %s", arg1);
|
||||
die_with_mount_error ("Can't mount tmpfs on %s", arg1);
|
||||
break;
|
||||
}
|
||||
|
||||
case PRIV_SEP_OP_DEVPTS_MOUNT:
|
||||
if (mount ("devpts", arg1, "devpts", MS_NOSUID | MS_NOEXEC,
|
||||
"newinstance,ptmxmode=0666,mode=620") != 0)
|
||||
die_with_error ("Can't mount devpts on %s", arg1);
|
||||
die_with_mount_error ("Can't mount devpts on %s", arg1);
|
||||
break;
|
||||
|
||||
case PRIV_SEP_OP_MQUEUE_MOUNT:
|
||||
if (mount ("mqueue", arg1, "mqueue", 0, NULL) != 0)
|
||||
die_with_error ("Can't mount mqueue on %s", arg1);
|
||||
die_with_mount_error ("Can't mount mqueue on %s", arg1);
|
||||
break;
|
||||
|
||||
case PRIV_SEP_OP_OVERLAY_MOUNT:
|
||||
if (mount ("overlay", arg2, "overlay", MS_MGC_VAL, arg1) != 0)
|
||||
{
|
||||
/* The standard message for ELOOP, "Too many levels of symbolic
|
||||
* links", is not helpful here. */
|
||||
if (errno == ELOOP)
|
||||
die ("Can't make overlay mount on %s with options %s: "
|
||||
"Overlay directories may not overlap",
|
||||
arg2, arg1);
|
||||
die_with_mount_error ("Can't make overlay mount on %s with options %s",
|
||||
arg2, arg1);
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIV_SEP_OP_SET_HOSTNAME:
|
||||
@@ -1170,6 +1190,7 @@ setup_newroot (bool unshare_pid,
|
||||
int privileged_op_socket)
|
||||
{
|
||||
SetupOp *op;
|
||||
int tmp_overlay_idx = 0;
|
||||
|
||||
for (op = ops; op != NULL; op = op->next)
|
||||
{
|
||||
@@ -1209,7 +1230,7 @@ setup_newroot (bool unshare_pid,
|
||||
parent_mode &= ~0005U;
|
||||
|
||||
dest = get_newroot_path (op->dest);
|
||||
if (mkdir_with_parents (dest, parent_mode, FALSE) != 0)
|
||||
if (mkdir_with_parents (dest, parent_mode, false) != 0)
|
||||
die_with_error ("Can't mkdir parents for %s", op->dest);
|
||||
}
|
||||
|
||||
@@ -1231,6 +1252,71 @@ setup_newroot (bool unshare_pid,
|
||||
(op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) |
|
||||
(op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0),
|
||||
0, 0, source, dest);
|
||||
|
||||
if (op->fd >= 0)
|
||||
{
|
||||
struct stat fd_st, mount_st;
|
||||
|
||||
/* When using bind-fd, there is a race condition between resolving the fd as a magic symlink
|
||||
* and mounting it, where someone could replace what is at the symlink target. Ideally
|
||||
* we would not even resolve the symlink and directly bind-mount from the fd, but unfortunately
|
||||
* we can't do that, because its not permitted to bind mount a fd from another user namespace.
|
||||
* So, we resolve, mount and then compare fstat+stat to detect the race. */
|
||||
|
||||
if (fstat(op->fd, &fd_st) != 0)
|
||||
die_with_error("Can't stat fd %d", op->fd);
|
||||
if (lstat(dest, &mount_st) != 0)
|
||||
die_with_error("Can't stat mount at %s", dest);
|
||||
|
||||
if (fd_st.st_ino != mount_st.st_ino ||
|
||||
fd_st.st_dev != mount_st.st_dev)
|
||||
die_with_error("Race condition binding dirfd");
|
||||
|
||||
close(op->fd);
|
||||
op->fd = -1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SETUP_OVERLAY_MOUNT:
|
||||
case SETUP_RO_OVERLAY_MOUNT:
|
||||
case SETUP_TMP_OVERLAY_MOUNT:
|
||||
{
|
||||
StringBuilder sb = {0};
|
||||
bool multi_src = false;
|
||||
|
||||
if (ensure_dir (dest, 0755) != 0)
|
||||
die_with_error ("Can't mkdir %s", op->dest);
|
||||
|
||||
if (op->source != NULL)
|
||||
{
|
||||
strappend (&sb, "upperdir=/oldroot");
|
||||
strappend_escape_for_mount_options (&sb, op->source);
|
||||
strappend (&sb, ",workdir=/oldroot");
|
||||
op = op->next;
|
||||
strappend_escape_for_mount_options (&sb, op->source);
|
||||
strappend (&sb, ",");
|
||||
}
|
||||
else if (op->type == SETUP_TMP_OVERLAY_MOUNT)
|
||||
strappendf (&sb, "upperdir=/tmp-overlay-upper-%1$d,workdir=/tmp-overlay-work-%1$d,",
|
||||
tmp_overlay_idx++);
|
||||
|
||||
strappend (&sb, "lowerdir=/oldroot");
|
||||
while (op->next != NULL && op->next->type == SETUP_OVERLAY_SRC)
|
||||
{
|
||||
op = op->next;
|
||||
if (multi_src)
|
||||
strappend (&sb, ":/oldroot");
|
||||
strappend_escape_for_mount_options (&sb, op->source);
|
||||
multi_src = true;
|
||||
}
|
||||
|
||||
strappend (&sb, ",userxattr");
|
||||
|
||||
privileged_op (privileged_op_socket,
|
||||
PRIV_SEP_OP_OVERLAY_MOUNT, 0, 0, 0, sb.str, dest);
|
||||
free (sb.str);
|
||||
}
|
||||
break;
|
||||
|
||||
case SETUP_REMOUNT_RO_NO_RECURSIVE:
|
||||
@@ -1468,7 +1554,25 @@ setup_newroot (bool unshare_pid,
|
||||
case SETUP_MAKE_SYMLINK:
|
||||
assert (op->source != NULL); /* guaranteed by the constructor */
|
||||
if (symlink (op->source, dest) != 0)
|
||||
die_with_error ("Can't make symlink at %s", op->dest);
|
||||
{
|
||||
if (errno == EEXIST)
|
||||
{
|
||||
cleanup_free char *existing = readlink_malloc (dest);
|
||||
if (existing == NULL)
|
||||
{
|
||||
if (errno == EINVAL)
|
||||
die ("Can't make symlink at %s: destination exists and is not a symlink", op->dest);
|
||||
else
|
||||
die_with_error ("Can't make symlink at %s: destination exists, and cannot read symlink target", op->dest);
|
||||
}
|
||||
|
||||
if (strcmp (existing, op->source) == 0)
|
||||
break;
|
||||
|
||||
die ("Can't make symlink at %s: existing destination is %s", op->dest, existing);
|
||||
}
|
||||
die_with_error ("Can't make symlink at %s", op->dest);
|
||||
}
|
||||
break;
|
||||
|
||||
case SETUP_SET_HOSTNAME:
|
||||
@@ -1478,6 +1582,7 @@ setup_newroot (bool unshare_pid,
|
||||
op->dest, NULL);
|
||||
break;
|
||||
|
||||
case SETUP_OVERLAY_SRC: /* handled by SETUP_OVERLAY_MOUNT */
|
||||
default:
|
||||
die ("Unexpected type %d", op->type);
|
||||
}
|
||||
@@ -1520,6 +1625,8 @@ resolve_symlinks_in_ops (void)
|
||||
case SETUP_RO_BIND_MOUNT:
|
||||
case SETUP_DEV_BIND_MOUNT:
|
||||
case SETUP_BIND_MOUNT:
|
||||
case SETUP_OVERLAY_SRC:
|
||||
case SETUP_OVERLAY_MOUNT:
|
||||
old_source = op->source;
|
||||
op->source = realpath (old_source, NULL);
|
||||
if (op->source == NULL)
|
||||
@@ -1531,6 +1638,8 @@ resolve_symlinks_in_ops (void)
|
||||
}
|
||||
break;
|
||||
|
||||
case SETUP_RO_OVERLAY_MOUNT:
|
||||
case SETUP_TMP_OVERLAY_MOUNT:
|
||||
case SETUP_MOUNT_PROC:
|
||||
case SETUP_MOUNT_DEV:
|
||||
case SETUP_MOUNT_TMPFS:
|
||||
@@ -1622,6 +1731,32 @@ warn_only_last_option (const char *name)
|
||||
warn ("Only the last %s option will take effect", name);
|
||||
}
|
||||
|
||||
static void
|
||||
make_setup_overlay_src_ops (const char *const *const argv)
|
||||
{
|
||||
/* SETUP_OVERLAY_SRC is unlike other SETUP_* ops in that it exists to hold
|
||||
* data for SETUP_{,TMP_,RO_}OVERLAY_MOUNT ops, not to be its own operation.
|
||||
* This lets us reuse existing code paths to handle resolving the realpaths
|
||||
* of each source, as no other operations involve multiple sources the way
|
||||
* the *_OVERLAY_MOUNT ops do.
|
||||
*
|
||||
* While the --overlay-src arguments are expected to (directly) precede the
|
||||
* --overlay argument, in bottom-to-top order, the SETUP_OVERLAY_SRC ops
|
||||
* follow their corresponding *_OVERLAY_MOUNT op, in top-to-bottom order
|
||||
* (the order in which overlayfs will want them). They are handled specially
|
||||
* in setup_new_root () during the processing of *_OVERLAY_MOUNT.
|
||||
*/
|
||||
int i;
|
||||
SetupOp *op;
|
||||
|
||||
for (i = 1; i <= next_overlay_src_count; i++)
|
||||
{
|
||||
op = setup_op_new (SETUP_OVERLAY_SRC);
|
||||
op->source = argv[1 - 2 * i];
|
||||
}
|
||||
next_overlay_src_count = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_args_recurse (int *argcp,
|
||||
const char ***argvp,
|
||||
@@ -1702,7 +1837,7 @@ parse_args_recurse (int *argcp,
|
||||
p++;
|
||||
}
|
||||
|
||||
data_argv = xcalloc (sizeof (char *) * (data_argc + 1));
|
||||
data_argv = xcalloc (data_argc + 1, sizeof (char *));
|
||||
|
||||
i = 0;
|
||||
p = opt_args_data;
|
||||
@@ -1717,7 +1852,7 @@ parse_args_recurse (int *argcp,
|
||||
}
|
||||
|
||||
data_argv_copy = data_argv; /* Don't change data_argv, we need to free it */
|
||||
parse_args_recurse (&data_argc, &data_argv_copy, TRUE, total_parsed_argc_p);
|
||||
parse_args_recurse (&data_argc, &data_argv_copy, true, total_parsed_argc_p);
|
||||
|
||||
argv += 1;
|
||||
argc -= 1;
|
||||
@@ -1734,6 +1869,10 @@ parse_args_recurse (int *argcp,
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
else if (strcmp (arg, "--level-prefix") == 0)
|
||||
{
|
||||
bwrap_level_prefix = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-all") == 0)
|
||||
{
|
||||
/* Keep this in order with the older (legacy) --unshare arguments,
|
||||
@@ -1742,45 +1881,45 @@ parse_args_recurse (int *argcp,
|
||||
*/
|
||||
opt_unshare_user_try = opt_unshare_ipc = opt_unshare_pid =
|
||||
opt_unshare_uts = opt_unshare_cgroup_try =
|
||||
opt_unshare_net = TRUE;
|
||||
opt_unshare_net = true;
|
||||
}
|
||||
/* Begin here the older individual --unshare variants */
|
||||
else if (strcmp (arg, "--unshare-user") == 0)
|
||||
{
|
||||
opt_unshare_user = TRUE;
|
||||
opt_unshare_user = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-user-try") == 0)
|
||||
{
|
||||
opt_unshare_user_try = TRUE;
|
||||
opt_unshare_user_try = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-ipc") == 0)
|
||||
{
|
||||
opt_unshare_ipc = TRUE;
|
||||
opt_unshare_ipc = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-pid") == 0)
|
||||
{
|
||||
opt_unshare_pid = TRUE;
|
||||
opt_unshare_pid = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-net") == 0)
|
||||
{
|
||||
opt_unshare_net = TRUE;
|
||||
opt_unshare_net = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-uts") == 0)
|
||||
{
|
||||
opt_unshare_uts = TRUE;
|
||||
opt_unshare_uts = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-cgroup") == 0)
|
||||
{
|
||||
opt_unshare_cgroup = TRUE;
|
||||
opt_unshare_cgroup = true;
|
||||
}
|
||||
else if (strcmp (arg, "--unshare-cgroup-try") == 0)
|
||||
{
|
||||
opt_unshare_cgroup_try = TRUE;
|
||||
opt_unshare_cgroup_try = true;
|
||||
}
|
||||
/* Begin here the newer --share variants */
|
||||
else if (strcmp (arg, "--share-net") == 0)
|
||||
{
|
||||
opt_unshare_net = FALSE;
|
||||
opt_unshare_net = false;
|
||||
}
|
||||
/* End --share variants, other arguments begin */
|
||||
else if (strcmp (arg, "--chdir") == 0)
|
||||
@@ -1797,11 +1936,11 @@ parse_args_recurse (int *argcp,
|
||||
}
|
||||
else if (strcmp (arg, "--disable-userns") == 0)
|
||||
{
|
||||
opt_disable_userns = TRUE;
|
||||
opt_disable_userns = true;
|
||||
}
|
||||
else if (strcmp (arg, "--assert-userns-disabled") == 0)
|
||||
{
|
||||
opt_assert_userns_disabled = TRUE;
|
||||
opt_assert_userns_disabled = true;
|
||||
}
|
||||
else if (strcmp (arg, "--remount-ro") == 0)
|
||||
{
|
||||
@@ -1859,6 +1998,100 @@ parse_args_recurse (int *argcp,
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
else if (strcmp (arg, "--bind-fd") == 0 ||
|
||||
strcmp (arg, "--ro-bind-fd") == 0)
|
||||
{
|
||||
int src_fd;
|
||||
char *endptr;
|
||||
|
||||
if (argc < 3)
|
||||
die ("--bind-fd takes two arguments");
|
||||
|
||||
src_fd = strtol (argv[1], &endptr, 10);
|
||||
if (argv[1][0] == 0 || endptr[0] != 0 || src_fd < 0)
|
||||
die ("Invalid fd: %s", argv[1]);
|
||||
|
||||
if (strcmp(arg, "--ro-bind-fd") == 0)
|
||||
op = setup_op_new (SETUP_RO_BIND_MOUNT);
|
||||
else
|
||||
op = setup_op_new (SETUP_BIND_MOUNT);
|
||||
op->source = xasprintf ("/proc/self/fd/%d", src_fd);
|
||||
op->fd = src_fd;
|
||||
op->dest = argv[2];
|
||||
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
else if (strcmp (arg, "--overlay-src") == 0)
|
||||
{
|
||||
if (is_privileged)
|
||||
die ("The --overlay-src option is not permitted in setuid mode");
|
||||
|
||||
next_overlay_src_count++;
|
||||
|
||||
argv += 1;
|
||||
argc -= 1;
|
||||
}
|
||||
else if (strcmp (arg, "--overlay") == 0)
|
||||
{
|
||||
SetupOp *workdir_op;
|
||||
|
||||
if (is_privileged)
|
||||
die ("The --overlay option is not permitted in setuid mode");
|
||||
|
||||
if (argc < 4)
|
||||
die ("--overlay takes three arguments");
|
||||
|
||||
if (next_overlay_src_count < 1)
|
||||
die ("--overlay requires at least one --overlay-src");
|
||||
|
||||
op = setup_op_new (SETUP_OVERLAY_MOUNT);
|
||||
op->source = argv[1];
|
||||
workdir_op = setup_op_new (SETUP_OVERLAY_SRC);
|
||||
workdir_op->source = argv[2];
|
||||
op->dest = argv[3];
|
||||
make_setup_overlay_src_ops (argv);
|
||||
|
||||
argv += 3;
|
||||
argc -= 3;
|
||||
}
|
||||
else if (strcmp (arg, "--tmp-overlay") == 0)
|
||||
{
|
||||
if (is_privileged)
|
||||
die ("The --tmp-overlay option is not permitted in setuid mode");
|
||||
|
||||
if (argc < 2)
|
||||
die ("--tmp-overlay takes an argument");
|
||||
|
||||
if (next_overlay_src_count < 1)
|
||||
die ("--tmp-overlay requires at least one --overlay-src");
|
||||
|
||||
op = setup_op_new (SETUP_TMP_OVERLAY_MOUNT);
|
||||
op->dest = argv[1];
|
||||
make_setup_overlay_src_ops (argv);
|
||||
opt_tmp_overlay_count++;
|
||||
|
||||
argv += 1;
|
||||
argc -= 1;
|
||||
}
|
||||
else if (strcmp (arg, "--ro-overlay") == 0)
|
||||
{
|
||||
if (is_privileged)
|
||||
die ("The --ro-overlay option is not permitted in setuid mode");
|
||||
|
||||
if (argc < 2)
|
||||
die ("--ro-overlay takes an argument");
|
||||
|
||||
if (next_overlay_src_count < 2)
|
||||
die ("--ro-overlay requires at least two --overlay-src");
|
||||
|
||||
op = setup_op_new (SETUP_RO_OVERLAY_MOUNT);
|
||||
op->dest = argv[1];
|
||||
make_setup_overlay_src_ops (argv);
|
||||
|
||||
argv += 1;
|
||||
argc -= 1;
|
||||
}
|
||||
else if (strcmp (arg, "--proc") == 0)
|
||||
{
|
||||
if (argc < 2)
|
||||
@@ -1907,7 +2140,7 @@ parse_args_recurse (int *argcp,
|
||||
|
||||
op = setup_op_new (SETUP_MOUNT_DEV);
|
||||
op->dest = argv[1];
|
||||
opt_needs_devpts = TRUE;
|
||||
opt_needs_devpts = true;
|
||||
|
||||
argv += 1;
|
||||
argc -= 1;
|
||||
@@ -2357,15 +2590,15 @@ parse_args_recurse (int *argcp,
|
||||
}
|
||||
else if (strcmp (arg, "--new-session") == 0)
|
||||
{
|
||||
opt_new_session = TRUE;
|
||||
opt_new_session = true;
|
||||
}
|
||||
else if (strcmp (arg, "--die-with-parent") == 0)
|
||||
{
|
||||
opt_die_with_parent = TRUE;
|
||||
opt_die_with_parent = true;
|
||||
}
|
||||
else if (strcmp (arg, "--as-pid-1") == 0)
|
||||
{
|
||||
opt_as_pid_1 = TRUE;
|
||||
opt_as_pid_1 = true;
|
||||
}
|
||||
else if (strcmp (arg, "--cap-add") == 0)
|
||||
{
|
||||
@@ -2373,7 +2606,7 @@ parse_args_recurse (int *argcp,
|
||||
if (argc < 2)
|
||||
die ("--cap-add takes an argument");
|
||||
|
||||
opt_cap_add_or_drop_used = TRUE;
|
||||
opt_cap_add_or_drop_used = true;
|
||||
|
||||
if (strcasecmp (argv[1], "ALL") == 0)
|
||||
{
|
||||
@@ -2399,7 +2632,7 @@ parse_args_recurse (int *argcp,
|
||||
if (argc < 2)
|
||||
die ("--cap-drop takes an argument");
|
||||
|
||||
opt_cap_add_or_drop_used = TRUE;
|
||||
opt_cap_add_or_drop_used = true;
|
||||
|
||||
if (strcasecmp (argv[1], "ALL") == 0)
|
||||
{
|
||||
@@ -2528,6 +2761,10 @@ parse_args_recurse (int *argcp,
|
||||
if (!is_modifier_option(arg) && next_size_arg != 0)
|
||||
die ("--size must be followed by --tmpfs");
|
||||
|
||||
/* Similarly for --overlay-src. */
|
||||
if (strcmp (arg, "--overlay-src") != 0 && next_overlay_src_count > 0)
|
||||
die ("--overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay");
|
||||
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
@@ -2542,7 +2779,10 @@ parse_args (int *argcp,
|
||||
{
|
||||
int total_parsed_argc = *argcp;
|
||||
|
||||
parse_args_recurse (argcp, argvp, FALSE, &total_parsed_argc);
|
||||
parse_args_recurse (argcp, argvp, false, &total_parsed_argc);
|
||||
|
||||
if (next_overlay_src_count > 0)
|
||||
die ("--overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2576,7 +2816,7 @@ namespace_ids_read (pid_t pid)
|
||||
NsInfo *info;
|
||||
|
||||
dir = xasprintf ("%d/ns", pid);
|
||||
ns_fd = openat (proc_fd, dir, O_PATH);
|
||||
ns_fd = TEMP_FAILURE_RETRY (openat (proc_fd, dir, O_PATH));
|
||||
|
||||
if (ns_fd < 0)
|
||||
die_with_error ("open /proc/%s/ns failed", dir);
|
||||
@@ -2588,7 +2828,7 @@ namespace_ids_read (pid_t pid)
|
||||
int r;
|
||||
|
||||
/* if we don't unshare this ns, ignore it */
|
||||
if (do_unshare && *do_unshare == FALSE)
|
||||
if (do_unshare && *do_unshare == false)
|
||||
continue;
|
||||
|
||||
r = fstatat (ns_fd, info->name, &st, 0);
|
||||
@@ -2623,7 +2863,7 @@ namespace_ids_write (int fd,
|
||||
output = xasprintf (",%s\"%s-namespace\": %ju",
|
||||
indent, info->name, nsid);
|
||||
|
||||
dump_info (fd, output, TRUE);
|
||||
dump_info (fd, output, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2648,6 +2888,7 @@ main (int argc,
|
||||
cleanup_free char *args_data UNUSED = NULL;
|
||||
int intermediate_pids_sockets[2] = {-1, -1};
|
||||
const char *exec_path = NULL;
|
||||
int i;
|
||||
|
||||
/* Handle --version early on before we try to acquire/drop
|
||||
* any capabilities so it works in a build environment;
|
||||
@@ -2731,18 +2972,18 @@ main (int argc,
|
||||
/* We have to do this if we weren't installed setuid (and we're not
|
||||
* root), so let's just DWIM */
|
||||
if (!is_privileged && getuid () != 0 && opt_userns_fd == -1)
|
||||
opt_unshare_user = TRUE;
|
||||
opt_unshare_user = true;
|
||||
|
||||
#ifdef ENABLE_REQUIRE_USERNS
|
||||
/* In this build option, we require userns. */
|
||||
if (is_privileged && getuid () != 0 && opt_userns_fd == -1)
|
||||
opt_unshare_user = TRUE;
|
||||
opt_unshare_user = true;
|
||||
#endif
|
||||
|
||||
if (opt_unshare_user_try &&
|
||||
stat ("/proc/self/ns/user", &sbuf) == 0)
|
||||
{
|
||||
bool disabled = FALSE;
|
||||
bool disabled = false;
|
||||
|
||||
/* RHEL7 has a kernel module parameter that lets you enable user namespaces */
|
||||
if (stat ("/sys/module/user_namespace/parameters/enable", &sbuf) == 0)
|
||||
@@ -2750,7 +2991,7 @@ main (int argc,
|
||||
cleanup_free char *enable = NULL;
|
||||
enable = load_file_at (AT_FDCWD, "/sys/module/user_namespace/parameters/enable");
|
||||
if (enable != NULL && enable[0] == 'N')
|
||||
disabled = TRUE;
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
/* Check for max_user_namespaces */
|
||||
@@ -2759,21 +3000,21 @@ main (int argc,
|
||||
cleanup_free char *max_user_ns = NULL;
|
||||
max_user_ns = load_file_at (AT_FDCWD, "/proc/sys/user/max_user_namespaces");
|
||||
if (max_user_ns != NULL && strcmp(max_user_ns, "0\n") == 0)
|
||||
disabled = TRUE;
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
/* Debian lets you disable *unprivileged* user namespaces. However this is not
|
||||
a problem if we're privileged, and if we're not opt_unshare_user is TRUE
|
||||
a problem if we're privileged, and if we're not opt_unshare_user is true
|
||||
already, and there is not much we can do, its just a non-working setup. */
|
||||
|
||||
if (!disabled)
|
||||
opt_unshare_user = TRUE;
|
||||
opt_unshare_user = true;
|
||||
}
|
||||
|
||||
if (argc <= 0)
|
||||
usage (EXIT_FAILURE, stderr);
|
||||
|
||||
__debug__ (("Creating root mount point\n"));
|
||||
debug ("Creating root mount point");
|
||||
|
||||
if (opt_sandbox_uid == (uid_t)-1)
|
||||
opt_sandbox_uid = real_uid;
|
||||
@@ -2797,7 +3038,7 @@ main (int argc,
|
||||
|
||||
/* We need to read stuff from proc during the pivot_root dance, etc.
|
||||
Lets keep a fd to it open */
|
||||
proc_fd = open ("/proc", O_PATH);
|
||||
proc_fd = TEMP_FAILURE_RETRY (open ("/proc", O_PATH));
|
||||
if (proc_fd == -1)
|
||||
die_with_error ("Can't open /proc");
|
||||
|
||||
@@ -2809,7 +3050,7 @@ main (int argc,
|
||||
* access ourselves. */
|
||||
base_path = "/tmp";
|
||||
|
||||
__debug__ (("creating new namespace\n"));
|
||||
debug ("creating new namespace");
|
||||
|
||||
if (opt_unshare_pid && !opt_as_pid_1)
|
||||
{
|
||||
@@ -2925,7 +3166,7 @@ main (int argc,
|
||||
*/
|
||||
write_uid_gid_map (ns_uid, real_uid,
|
||||
ns_gid, real_gid,
|
||||
pid, TRUE, opt_needs_devpts);
|
||||
pid, true, opt_needs_devpts);
|
||||
}
|
||||
|
||||
/* Initial launched process, wait for pid 1 or exec:ed command to exit */
|
||||
@@ -2934,7 +3175,7 @@ main (int argc,
|
||||
die_with_error ("Setting userns2 failed");
|
||||
|
||||
/* We don't need any privileges in the launcher, drop them immediately. */
|
||||
drop_privs (FALSE, FALSE);
|
||||
drop_privs (false, false);
|
||||
|
||||
/* Optionally bind our lifecycle to that of the parent */
|
||||
handle_die_with_parent ();
|
||||
@@ -2942,17 +3183,17 @@ main (int argc,
|
||||
if (opt_info_fd != -1)
|
||||
{
|
||||
cleanup_free char *output = xasprintf ("{\n \"child-pid\": %i", pid);
|
||||
dump_info (opt_info_fd, output, TRUE);
|
||||
namespace_ids_write (opt_info_fd, FALSE);
|
||||
dump_info (opt_info_fd, "\n}\n", TRUE);
|
||||
dump_info (opt_info_fd, output, true);
|
||||
namespace_ids_write (opt_info_fd, false);
|
||||
dump_info (opt_info_fd, "\n}\n", true);
|
||||
close (opt_info_fd);
|
||||
}
|
||||
if (opt_json_status_fd != -1)
|
||||
{
|
||||
cleanup_free char *output = xasprintf ("{ \"child-pid\": %i", pid);
|
||||
dump_info (opt_json_status_fd, output, TRUE);
|
||||
namespace_ids_write (opt_json_status_fd, TRUE);
|
||||
dump_info (opt_json_status_fd, " }\n", TRUE);
|
||||
dump_info (opt_json_status_fd, output, true);
|
||||
namespace_ids_write (opt_json_status_fd, true);
|
||||
dump_info (opt_json_status_fd, " }\n", true);
|
||||
}
|
||||
|
||||
if (opt_userns_block_fd != -1)
|
||||
@@ -2964,7 +3205,7 @@ main (int argc,
|
||||
|
||||
/* Let child run now that the uid maps are set up */
|
||||
val = 1;
|
||||
res = write (child_wait_fd, &val, 8);
|
||||
res = TEMP_FAILURE_RETRY (write (child_wait_fd, &val, 8));
|
||||
/* Ignore res, if e.g. the child died and closed child_wait_fd we don't want to error out here */
|
||||
close (child_wait_fd);
|
||||
|
||||
@@ -3048,7 +3289,7 @@ main (int argc,
|
||||
|
||||
write_uid_gid_map (ns_uid, real_uid,
|
||||
ns_gid, real_gid,
|
||||
-1, TRUE, FALSE);
|
||||
-1, true, false);
|
||||
}
|
||||
|
||||
old_umask = umask (0);
|
||||
@@ -3060,11 +3301,11 @@ main (int argc,
|
||||
* receive mounts from the real root, but don't
|
||||
* propagate mounts to the real root. */
|
||||
if (mount (NULL, "/", NULL, MS_SILENT | MS_SLAVE | MS_REC, NULL) < 0)
|
||||
die_with_error ("Failed to make / slave");
|
||||
die_with_mount_error ("Failed to make / slave");
|
||||
|
||||
/* Create a tmpfs which we will use as / in the namespace */
|
||||
if (mount ("tmpfs", base_path, "tmpfs", MS_NODEV | MS_NOSUID, NULL) != 0)
|
||||
die_with_error ("Failed to mount tmpfs");
|
||||
die_with_mount_error ("Failed to mount tmpfs");
|
||||
|
||||
old_cwd = get_current_dir_name ();
|
||||
|
||||
@@ -3083,11 +3324,24 @@ main (int argc,
|
||||
die_with_error ("Creating newroot failed");
|
||||
|
||||
if (mount ("newroot", "newroot", NULL, MS_SILENT | MS_MGC_VAL | MS_BIND | MS_REC, NULL) < 0)
|
||||
die_with_error ("setting up newroot bind");
|
||||
die_with_mount_error ("setting up newroot bind");
|
||||
|
||||
if (mkdir ("oldroot", 0755))
|
||||
die_with_error ("Creating oldroot failed");
|
||||
|
||||
for (i = 0; i < opt_tmp_overlay_count; i++)
|
||||
{
|
||||
char *dirname;
|
||||
dirname = xasprintf ("tmp-overlay-upper-%d", i);
|
||||
if (mkdir (dirname, 0755))
|
||||
die_with_error ("Creating --tmp-overlay upperdir failed");
|
||||
free (dirname);
|
||||
dirname = xasprintf ("tmp-overlay-work-%d", i);
|
||||
if (mkdir (dirname, 0755))
|
||||
die_with_error ("Creating --tmp-overlay workdir failed");
|
||||
free (dirname);
|
||||
}
|
||||
|
||||
if (pivot_root (base_path, "oldroot"))
|
||||
die_with_error ("pivot_root");
|
||||
|
||||
@@ -3109,7 +3363,7 @@ main (int argc,
|
||||
if (child == 0)
|
||||
{
|
||||
/* Unprivileged setup process */
|
||||
drop_privs (FALSE, TRUE);
|
||||
drop_privs (false, true);
|
||||
close (privsep_sockets[0]);
|
||||
setup_newroot (opt_unshare_pid, privsep_sockets[1]);
|
||||
exit (0);
|
||||
@@ -3131,12 +3385,12 @@ main (int argc,
|
||||
op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer),
|
||||
&flags, &perms, &size_arg, &arg1, &arg2);
|
||||
privileged_op (-1, op, flags, perms, size_arg, arg1, arg2);
|
||||
if (write (unpriv_socket, buffer, 1) != 1)
|
||||
if (TEMP_FAILURE_RETRY (write (unpriv_socket, buffer, 1)) != 1)
|
||||
die ("Can't write to op_socket");
|
||||
}
|
||||
while (op != PRIV_SEP_OP_DONE);
|
||||
|
||||
waitpid (child, &status, 0);
|
||||
TEMP_FAILURE_RETRY (waitpid (child, &status, 0));
|
||||
/* Continue post setup */
|
||||
}
|
||||
}
|
||||
@@ -3149,7 +3403,7 @@ main (int argc,
|
||||
|
||||
/* The old root better be rprivate or we will send unmount events to the parent namespace */
|
||||
if (mount ("oldroot", "oldroot", NULL, MS_SILENT | MS_REC | MS_PRIVATE, NULL) != 0)
|
||||
die_with_error ("Failed to make old root rprivate");
|
||||
die_with_mount_error ("Failed to make old root rprivate");
|
||||
|
||||
if (umount2 ("oldroot", MNT_DETACH))
|
||||
die_with_error ("unmount old root");
|
||||
@@ -3160,7 +3414,7 @@ main (int argc,
|
||||
* We're aiming to make /newroot the real root, and get rid of /oldroot. To do
|
||||
* that we need a temporary place to store it before we can unmount it.
|
||||
*/
|
||||
{ cleanup_fd int oldrootfd = open ("/", O_DIRECTORY | O_RDONLY);
|
||||
{ cleanup_fd int oldrootfd = TEMP_FAILURE_RETRY (open ("/", O_DIRECTORY | O_RDONLY));
|
||||
if (oldrootfd < 0)
|
||||
die_with_error ("can't open /");
|
||||
if (chdir ("/newroot") != 0)
|
||||
@@ -3208,7 +3462,7 @@ main (int argc,
|
||||
{
|
||||
cleanup_fd int sysctl_fd = -1;
|
||||
|
||||
sysctl_fd = openat (proc_fd, "sys/user/max_user_namespaces", O_WRONLY);
|
||||
sysctl_fd = TEMP_FAILURE_RETRY (openat (proc_fd, "sys/user/max_user_namespaces", O_WRONLY));
|
||||
|
||||
if (sysctl_fd < 0)
|
||||
die_with_error ("cannot open /proc/sys/user/max_user_namespaces");
|
||||
@@ -3221,11 +3475,11 @@ main (int argc,
|
||||
die_with_error ("unshare user ns");
|
||||
|
||||
/* We're in a new user namespace, we got back the bounding set, clear it again */
|
||||
drop_cap_bounding_set (FALSE);
|
||||
drop_cap_bounding_set (false);
|
||||
|
||||
write_uid_gid_map (opt_sandbox_uid, ns_uid,
|
||||
opt_sandbox_gid, ns_gid,
|
||||
-1, FALSE, FALSE);
|
||||
-1, false, false);
|
||||
}
|
||||
|
||||
if (opt_disable_userns || opt_assert_userns_disabled)
|
||||
@@ -3238,7 +3492,7 @@ main (int argc,
|
||||
}
|
||||
|
||||
/* All privileged ops are done now, so drop caps we don't need */
|
||||
drop_privs (!is_privileged, TRUE);
|
||||
drop_privs (!is_privileged, true);
|
||||
|
||||
if (opt_block_fd != -1)
|
||||
{
|
||||
@@ -3286,7 +3540,7 @@ main (int argc,
|
||||
if (label_exec (opt_exec_label) == -1)
|
||||
die_with_error ("label_exec %s", argv[0]);
|
||||
|
||||
__debug__ (("forking for child\n"));
|
||||
debug ("forking for child");
|
||||
|
||||
if (!opt_as_pid_1 && (opt_unshare_pid || lock_files != NULL || opt_sync_fd != -1))
|
||||
{
|
||||
@@ -3302,7 +3556,7 @@ main (int argc,
|
||||
|
||||
if (pid != 0)
|
||||
{
|
||||
drop_all_caps (FALSE);
|
||||
drop_all_caps (false);
|
||||
|
||||
/* Close fds in pid 1, except stdio and optionally event_fd
|
||||
(for syncing pid 2 lifetime with monitor_child) and
|
||||
@@ -3324,7 +3578,7 @@ main (int argc,
|
||||
}
|
||||
}
|
||||
|
||||
__debug__ (("launch executable %s\n", argv[0]));
|
||||
debug ("launch executable %s", argv[0]);
|
||||
|
||||
if (proc_fd != -1)
|
||||
close (proc_fd);
|
||||
|
111
bwrap.xml
111
bwrap.xml
@@ -96,6 +96,26 @@
|
||||
<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><4></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>
|
||||
@@ -297,6 +317,86 @@
|
||||
<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>
|
||||
@@ -364,7 +464,16 @@
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--symlink <arg choice="plain">SRC</arg> <arg choice="plain">DEST</arg></option></term>
|
||||
<listitem><para>Create a symlink at <arg choice="plain">DEST</arg> with target <arg choice="plain">SRC</arg></para></listitem>
|
||||
<listitem>
|
||||
<para>Create a symlink at <arg choice="plain">DEST</arg> with target
|
||||
<arg choice="plain">SRC</arg>.</para>
|
||||
<para>Since version 0.9.0, it is not considered to be an error if
|
||||
<arg choice="plain">DEST</arg> already exists as a symbolic link and its
|
||||
target is exactly <arg choice="plain">SRC</arg>.</para>
|
||||
<para>Before version 0.9.0, if <arg choice="plain">DEST</arg> already
|
||||
existed, this would be treated as an error (even if its target
|
||||
was identical to <arg choice="plain">SRC</arg>).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--chmod <arg choice="plain">OCTAL</arg> <arg choice="plain">PATH</arg></option></term>
|
||||
|
@@ -56,8 +56,6 @@ 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 \
|
||||
@@ -81,8 +79,6 @@ if command -v yum; then
|
||||
yum -y install \
|
||||
'pkgconfig(libselinux)' \
|
||||
/usr/bin/eu-readelf \
|
||||
autoconf \
|
||||
automake \
|
||||
docbook-style-xsl \
|
||||
gcc \
|
||||
git \
|
||||
|
@@ -51,15 +51,19 @@ _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
|
||||
|
@@ -13,7 +13,6 @@ if bash_completion_dir == ''
|
||||
default_value: '',
|
||||
pkgconfig: 'completionsdir',
|
||||
pkgconfig_define: [
|
||||
'prefix', get_option('prefix'),
|
||||
'datadir', get_option('prefix') / get_option('datadir'),
|
||||
],
|
||||
)
|
||||
|
158
configure.ac
158
configure.ac
@@ -1,158 +0,0 @@
|
||||
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
348
git.mk
@@ -1,348 +0,0 @@
|
||||
# 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
292
m4/attributes.m4
@@ -1,292 +0,0 @@
|
||||
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
|
||||
])
|
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'bubblewrap',
|
||||
'c',
|
||||
version : '0.8.0',
|
||||
version : '0.11.0',
|
||||
meson_version : '>=0.49.0',
|
||||
default_options : [
|
||||
'warning_level=2',
|
||||
|
@@ -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',
|
||||
|
@@ -53,8 +53,8 @@ rtnl_send_request (int rtnl_fd,
|
||||
struct sockaddr_nl dst_addr = { AF_NETLINK, 0 };
|
||||
ssize_t sent;
|
||||
|
||||
sent = sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0,
|
||||
(struct sockaddr *) &dst_addr, sizeof (dst_addr));
|
||||
sent = TEMP_FAILURE_RETRY (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 = recv (rtnl_fd, buffer, sizeof (buffer), 0);
|
||||
received = TEMP_FAILURE_RETRY (recv (rtnl_fd, buffer, sizeof (buffer), 0));
|
||||
if (received < 0)
|
||||
return -1;
|
||||
|
||||
|
@@ -1,18 +1,18 @@
|
||||
bubblewrap release checklist
|
||||
============================
|
||||
|
||||
* Collect release notes
|
||||
* Update version number in `configure.ac` **and** `meson.build`
|
||||
* Collect release notes in `NEWS`
|
||||
* Update version number in `meson.build` and release date in `NEWS`
|
||||
* Commit the changes
|
||||
* `make distcheck`
|
||||
* `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
|
||||
* Include the release notes from `NEWS` 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 `make distcheck`
|
||||
* Upload the tarball that you built with `meson dist`
|
||||
* Get the `sha256sum` of the tarball and append it to the description
|
||||
* `Publish release`
|
||||
|
@@ -76,6 +76,21 @@ 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"
|
||||
@@ -94,7 +109,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 --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 -c "%n %i" /proc/self/ns/*' 42>info.json 43>json-status.json 2>err.txt)
|
||||
|
||||
for NS in "ipc" "mnt" "net" "pid" "uts"; do
|
||||
|
||||
@@ -550,4 +565,128 @@ $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
|
||||
|
@@ -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,6 +200,37 @@ 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)
|
||||
@@ -210,6 +241,7 @@ 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
271
utils.c
@@ -19,8 +19,11 @@
|
||||
#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
|
||||
@@ -32,21 +35,34 @@
|
||||
#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)
|
||||
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)
|
||||
{
|
||||
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
|
||||
warn (const char *format, ...)
|
||||
bwrap_log (int severity,
|
||||
const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start (args, format);
|
||||
warnv (format, args);
|
||||
bwrap_logv (severity, format, args, NULL);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
@@ -58,13 +74,24 @@ die_with_error (const char *format, ...)
|
||||
|
||||
errsv = errno;
|
||||
|
||||
fprintf (stderr, "bwrap: ");
|
||||
|
||||
va_start (args, format);
|
||||
vfprintf (stderr, format, args);
|
||||
bwrap_logv (LOG_ERR, format, args, strerror (errsv));
|
||||
va_end (args);
|
||||
|
||||
fprintf (stderr, ": %s\n", strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
void
|
||||
die_with_mount_error (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int errsv;
|
||||
|
||||
errsv = errno;
|
||||
|
||||
va_start (args, format);
|
||||
bwrap_logv (LOG_ERR, format, args, mount_strerror (errsv));
|
||||
va_end (args);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
@@ -75,7 +102,7 @@ die (const char *format, ...)
|
||||
va_list args;
|
||||
|
||||
va_start (args, format);
|
||||
warnv (format, args);
|
||||
bwrap_logv (LOG_ERR, format, args, NULL);
|
||||
va_end (args);
|
||||
|
||||
exit (1);
|
||||
@@ -126,9 +153,9 @@ xmalloc (size_t size)
|
||||
}
|
||||
|
||||
void *
|
||||
xcalloc (size_t size)
|
||||
xcalloc (size_t nmemb, size_t size)
|
||||
{
|
||||
void *res = calloc (1, size);
|
||||
void *res = calloc (nmemb, size);
|
||||
|
||||
if (res == NULL)
|
||||
die_oom ();
|
||||
@@ -138,9 +165,13 @@ xcalloc (size_t size)
|
||||
void *
|
||||
xrealloc (void *ptr, size_t size)
|
||||
{
|
||||
void *res = realloc (ptr, size);
|
||||
void *res;
|
||||
|
||||
if (size != 0 && res == NULL)
|
||||
assert (size != 0);
|
||||
|
||||
res = realloc (ptr, size);
|
||||
|
||||
if (res == NULL)
|
||||
die_oom ();
|
||||
return res;
|
||||
}
|
||||
@@ -183,7 +214,7 @@ bool
|
||||
has_path_prefix (const char *str,
|
||||
const char *prefix)
|
||||
{
|
||||
while (TRUE)
|
||||
while (true)
|
||||
{
|
||||
/* Skip consecutive slashes to reach next path
|
||||
element */
|
||||
@@ -194,13 +225,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++;
|
||||
}
|
||||
@@ -208,7 +239,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +247,7 @@ bool
|
||||
path_equal (const char *path1,
|
||||
const char *path2)
|
||||
{
|
||||
while (TRUE)
|
||||
while (true)
|
||||
{
|
||||
/* Skip consecutive slashes to reach next path
|
||||
element */
|
||||
@@ -233,14 +264,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +378,7 @@ fdwalk (int proc_fd, int (*cb)(void *data,
|
||||
int res = 0;
|
||||
DIR *d;
|
||||
|
||||
dfd = openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
|
||||
dfd = TEMP_FAILURE_RETRY (openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY));
|
||||
if (dfd == -1)
|
||||
return res;
|
||||
|
||||
@@ -421,7 +452,7 @@ write_to_fd (int fd,
|
||||
|
||||
/* Sets errno on error (!= 0), ENOSPC on short write */
|
||||
int
|
||||
write_file_at (int dirfd,
|
||||
write_file_at (int dfd,
|
||||
const char *path,
|
||||
const char *content)
|
||||
{
|
||||
@@ -429,7 +460,7 @@ write_file_at (int dirfd,
|
||||
bool res;
|
||||
int errsv;
|
||||
|
||||
fd = openat (dirfd, path, O_RDWR | O_CLOEXEC, 0);
|
||||
fd = TEMP_FAILURE_RETRY (openat (dfd, path, O_RDWR | O_CLOEXEC, 0));
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
@@ -454,7 +485,7 @@ create_file (const char *path,
|
||||
int res;
|
||||
int errsv;
|
||||
|
||||
fd = creat (path, mode);
|
||||
fd = TEMP_FAILURE_RETRY (creat (path, mode));
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
@@ -503,7 +534,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)
|
||||
@@ -535,11 +566,11 @@ copy_file (const char *src_path,
|
||||
int res;
|
||||
int errsv;
|
||||
|
||||
sfd = open (src_path, O_CLOEXEC | O_RDONLY);
|
||||
sfd = TEMP_FAILURE_RETRY (open (src_path, O_CLOEXEC | O_RDONLY));
|
||||
if (sfd == -1)
|
||||
return -1;
|
||||
|
||||
dfd = creat (dst_path, mode);
|
||||
dfd = TEMP_FAILURE_RETRY (creat (dst_path, mode));
|
||||
if (dfd == -1)
|
||||
{
|
||||
errsv = errno;
|
||||
@@ -577,6 +608,12 @@ load_file_data (int fd,
|
||||
{
|
||||
if (data_len == data_read + 1)
|
||||
{
|
||||
if (data_len > SSIZE_MAX / 2)
|
||||
{
|
||||
errno = EFBIG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_len *= 2;
|
||||
data = xrealloc (data, data_len);
|
||||
}
|
||||
@@ -603,14 +640,14 @@ load_file_data (int fd,
|
||||
/* Sets errno on error (== NULL),
|
||||
* Always ensures terminating zero */
|
||||
char *
|
||||
load_file_at (int dirfd,
|
||||
load_file_at (int dfd,
|
||||
const char *path)
|
||||
{
|
||||
int fd;
|
||||
char *data;
|
||||
int errsv;
|
||||
|
||||
fd = openat (dirfd, path, O_CLOEXEC | O_RDONLY);
|
||||
fd = TEMP_FAILURE_RETRY (openat (dfd, path, O_CLOEXEC | O_RDONLY));
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
@@ -716,15 +753,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 socket)
|
||||
send_pid_on_socket (int sockfd)
|
||||
{
|
||||
char buf[1] = { 0 };
|
||||
struct msghdr msg = {};
|
||||
struct iovec iov = { buf, sizeof (buf) };
|
||||
const ssize_t control_len_snd = CMSG_SPACE(sizeof(struct ucred));
|
||||
char control_buf_snd[control_len_snd];
|
||||
_Alignas(struct cmsghdr) char control_buf_snd[control_len_snd];
|
||||
struct cmsghdr *cmsg;
|
||||
struct ucred *cred;
|
||||
struct ucred cred;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
@@ -735,13 +772,13 @@ send_pid_on_socket (int socket)
|
||||
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 ();
|
||||
cred.pid = getpid ();
|
||||
cred.uid = geteuid ();
|
||||
cred.gid = getegid ();
|
||||
memcpy (CMSG_DATA (cmsg), &cred, sizeof (cred));
|
||||
|
||||
if (sendmsg (socket, &msg, 0) < 0)
|
||||
if (TEMP_FAILURE_RETRY (sendmsg (sockfd, &msg, 0)) < 0)
|
||||
die_with_error ("Can't send pid");
|
||||
}
|
||||
|
||||
@@ -758,13 +795,13 @@ create_pid_socketpair (int sockets[2])
|
||||
}
|
||||
|
||||
int
|
||||
read_pid_from_socket (int socket)
|
||||
read_pid_from_socket (int sockfd)
|
||||
{
|
||||
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));
|
||||
char control_buf_rcv[control_len_rcv];
|
||||
_Alignas(struct cmsghdr) char control_buf_rcv[control_len_rcv];
|
||||
struct cmsghdr* cmsg;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
@@ -772,7 +809,7 @@ read_pid_from_socket (int socket)
|
||||
msg.msg_control = control_buf_rcv;
|
||||
msg.msg_controllen = control_len_rcv;
|
||||
|
||||
if (recvmsg (socket, &msg, 0) < 0)
|
||||
if (TEMP_FAILURE_RETRY (recvmsg (sockfd, &msg, 0)) < 0)
|
||||
die_with_error ("Can't read pid from socket");
|
||||
|
||||
if (msg.msg_controllen <= 0)
|
||||
@@ -785,8 +822,10 @@ read_pid_from_socket (int socket)
|
||||
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||
payload_len == sizeof(struct ucred))
|
||||
{
|
||||
struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
|
||||
return cred->pid;
|
||||
struct ucred cred;
|
||||
|
||||
memcpy (&cred, CMSG_DATA (cmsg), sizeof (cred));
|
||||
return cred.pid;
|
||||
}
|
||||
}
|
||||
die ("No pid returned on socket");
|
||||
@@ -803,6 +842,8 @@ 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);
|
||||
@@ -891,3 +932,149 @@ 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++;
|
||||
}
|
||||
}
|
||||
|
49
utils.h
49
utils.h
@@ -24,26 +24,33 @@
|
||||
#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__(x) printf x
|
||||
#define debug(...) bwrap_log (LOG_DEBUG, __VA_ARGS__)
|
||||
#else
|
||||
#define __debug__(x)
|
||||
#define debug(...)
|
||||
#endif
|
||||
|
||||
#define UNUSED __attribute__((__unused__))
|
||||
|
||||
#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
typedef int bool;
|
||||
#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 PIPE_READ_END 0
|
||||
#define PIPE_WRITE_END 1
|
||||
@@ -52,10 +59,17 @@ typedef int bool;
|
||||
#define PR_SET_CHILD_SUBREAPER 36
|
||||
#endif
|
||||
|
||||
void warn (const char *format,
|
||||
...) __attribute__((format (printf, 1, 2)));
|
||||
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 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__));
|
||||
@@ -64,7 +78,7 @@ void die_unless_label_valid (const char *label);
|
||||
void fork_intermediate_child (void);
|
||||
|
||||
void *xmalloc (size_t size);
|
||||
void *xcalloc (size_t size);
|
||||
void *xcalloc (size_t nmemb, size_t size);
|
||||
void *xrealloc (void *ptr,
|
||||
size_t size);
|
||||
char *xstrdup (const char *str);
|
||||
@@ -134,6 +148,8 @@ char *label_mount (const char *opt,
|
||||
int label_exec (const char *exec_label);
|
||||
int label_create_file (const char *file_label);
|
||||
|
||||
const char *mount_strerror (int errsv);
|
||||
|
||||
static inline void
|
||||
cleanup_freep (void *p)
|
||||
{
|
||||
@@ -182,3 +198,20 @@ 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);
|
||||
|
Reference in New Issue
Block a user