[PATCH] Use guestfsd binary to auto-generate library dependencies for appliance
by Hilko Bengen
The ELF NEEDED are used to determine guestfsd's library dependencies
with help from the dynamic linker and the package manager.
This was prompted by Debian bug #972241 which was caused by a
libtirpc package renaming in Debian/unstable because the SONAME had
been changed.
---
appliance/Makefile.am | 26 ++++++++++++++++-
appliance/packagelist.in | 62 ++--------------------------------------
m4/guestfs-appliance.m4 | 9 ++++++
3 files changed, 36 insertions(+), 61 deletions(-)
diff --git a/appliance/Makefile.am b/appliance/Makefile.am
index bad5d9d9eb..a213e12be4 100644
--- a/appliance/Makefile.am
+++ b/appliance/Makefile.am
@@ -79,7 +79,30 @@ make.sh: make.sh.in $(top_builddir)/config.log $(top_builddir)/config.status
PACKAGELIST_CPP_FLAGS = -D$(DISTRO)=1 -DEXTRA_PACKAGES="$(EXTRA_PACKAGES)"
-packagelist: packagelist.in Makefile
+
+if HAVE_RPM
+QUERY_FILES_CMD := xargs rpm -qf --qf '%{name}\n'
+endif
+if HAVE_DPKG
+QUERY_FILES_CMD := xargs dpkg -S | cut -d: -f1
+endif
+if HAVE_PACMAN
+QUERY_FILES_CMD := xargs pacman -Qo | sed -r 's/.* is owned by ([^ ]+) .*/\1/'
+endif
+
+# Automatically generate library dependency list
+guestfsd.deps: ../daemon/guestfsd
+ /sbin/ldconfig -p > ld.so.cache.txt
+ objdump -p $^ |\
+ sed -ne '/NEEDED/{s/ *NEEDED *//; p;}' |\
+ xargs -i grep -F {} ld.so.cache.txt |\
+ sed -ne '/ => /{s/.* => *//; p;}' |\
+ $(QUERY_FILES_CMD) |\
+ sort -u > $@.t
+ rm -f ld.so.cache.txt
+ mv $@.t $@
+
+packagelist: packagelist.in Makefile guestfsd.deps
m4 $(PACKAGELIST_CPP_FLAGS) $< | \
grep -v '^[[:space:]]*$$' | grep -v '^#' > $@-t
cmp -s $@ $@-t || mv $@-t $@
@@ -153,5 +176,6 @@ stamp-libguestfs-make-fixed-appliance.pod: libguestfs-make-fixed-appliance.pod
DISTCLEANFILES += \
make.sh \
+ guestfsd.deps \
packagelist \
supermin.d/*
diff --git a/appliance/packagelist.in b/appliance/packagelist.in
index 13c83d8e45..68ed16a415 100644
--- a/appliance/packagelist.in
+++ b/appliance/packagelist.in
@@ -23,7 +23,6 @@ dnl Basically the same with a few minor tweaks.
ifelse(UBUNTU,1,`define(`DEBIAN',1)')
ifelse(REDHAT,1,
- augeas-libs
cryptsetup
cryptsetup-luks dnl old name used before Fedora 17
dhclient
@@ -32,28 +31,20 @@ ifelse(REDHAT,1,
gfs2-utils
grub
hfsplus-tools
- hivex
iproute
iputils
- jansson
kernel
- libcap
- libldm
- libtirpc
nilfs-utils
ntfsprogs
ntfs-3g
ntfs-3g-system-compression
openssh-clients
- pcre
policycoreutils
reiserfs-utils
- libselinux
syslinux-extlinux
systemd dnl for /sbin/reboot and udevd
vim-minimal
xz
- yara
zfs-fuse
)
@@ -79,17 +70,7 @@ dnl iproute has been renamed to iproute2
iputils-tracepath
isc-dhcp-client
ldmtool
- libaugeas0
libc-bin
- libcap2
- libhivex0
- libjansson4
- libpcre3
- libsystemd0
- libsystemd-id128-0
- libsystemd-journal0
- libtirpc1
- libyara3
linux-image
dnl syslinux 'suggests' mtools, but in reality it's a hard dependency:
mtools
@@ -107,19 +88,14 @@ dnl iproute has been renamed to iproute2
)
ifelse(ARCHLINUX,1,
- augeas
cdrkit
cdrtools
cryptsetup
dhcpcd
gptfdisk
grub
- hivex
iproute2
iputils
- jansson
- libcap
- libtirpc
linux
lrzip
dnl syslinux has mtools as optional dependency, but in reality it's
@@ -129,16 +105,13 @@ ifelse(ARCHLINUX,1,
nilfs-utils
ntfs-3g
ntfs-3g-system-compression
- pcre
reiserfsprogs
systemd
vim
xz
- yara
)
ifelse(SUSE,1,
- augeas
dnl It seems no other augeas package depends on it.
augeas-lenses
btrfsprogs
@@ -149,16 +122,9 @@ ifelse(SUSE,1,
genisoimage
glibc-locale
gptfdisk
- hivex
initviocons
iproute2
iputils
- libcap2
- libhivex0
- libjansson4
- libselinux1
- libtirpc3
- libyara3
mkisofs
ntfsprogs
ntfs-3g
@@ -167,11 +133,9 @@ ifelse(SUSE,1,
systemd
vim
xz
- yara
)
ifelse(FRUGALWARE,1,
- augeas
cryptsetup-luks
cdrkit
dhclient
@@ -179,14 +143,10 @@ ifelse(FRUGALWARE,1,
hfsplus
iproute2
iputils
- jansson
kernel
- libcap
- libtirpc
ntfsprogs
ntfs-3g
openssh
- pcre
reiserfsprogs
systemd
vim
@@ -197,7 +157,6 @@ ifelse(FRUGALWARE,1,
)
ifelse(MAGEIA,1,
- augeas
cryptsetup
chkconfig /* for /etc/init.d */
cdrkit-genisoimage
@@ -207,30 +166,21 @@ ifelse(MAGEIA,1,
gfs2-utils
grub
hfsplus-tools
- hivex
iproute2
iputils
- libcap
- libjansson4
- lib64jansson4 /* lib64jansson4 does not provide libjansson4 */
- libldm
- libtirpc
dnl syslinux uses mtools without depending on it
mtools
nilfs-utils
ntfsprogs
ntfs-3g
openssh-clients
- pcre
reiserfs-utils
- libselinux
systemd /* for /sbin/reboot and udevd */
vim-minimal
xz
)
ifelse(OPENMANDRIVA,1,
- augeas
cryptsetup
chkconfig /* for /etc/init.d */
cdrkit-genisoimage
@@ -238,27 +188,20 @@ ifelse(OPENMANDRIVA,1,
dhcp-client
extlinux
grub2
- lib64hivex0
- hivex
iproute2
iputils
- libcap
- libjansson4
- lib64jansson4 /* lib64jansson4 does not provide libjansson4 */
- libldm
- libtirpc
dnl syslinux uses mtools without depending on it
mtools
nilfs-utils
ntfs-3g
openssh-clients
- pcre
- libselinux
systemd /* for /sbin/reboot and udevd */
vim-minimal
xz
)
+include(guestfsd.deps)
+
acl
attr
bash
@@ -280,7 +223,6 @@ gzip
jfsutils
kmod
less
-libxml2
lsof
lsscsi
lvm2
diff --git a/m4/guestfs-appliance.m4 b/m4/guestfs-appliance.m4
index a7f7d8321a..b0a6ab40f0 100644
--- a/m4/guestfs-appliance.m4
+++ b/m4/guestfs-appliance.m4
@@ -114,6 +114,15 @@ AC_ARG_WITH([distro],
AC_MSG_ERROR([/etc/os-release not available, please specify the distro using --with-distro=DISTRO])
fi
]
+ AM_CONDITIONAL([HAVE_RPM],
+ [AS_CASE([$DISTRO], [REDHAT | SUSE | OPENMANDRIVA | MAGEIA ], [true],
+ [*], [false])])
+ AM_CONDITIONAL([HAVE_DPKG],
+ [AS_CASE([$DISTRO], [DEBIAN | UBUNTU ], [true],
+ [*], [false])])
+ AM_CONDITIONAL([HAVE_PACMAN],
+ [AS_CASE([$DISTRO], [ARCHLINUX | FRUGALWARE ], [true],
+ [*], [false])])
)
AC_SUBST([DISTRO])
--
2.28.0
4 years, 1 month
Re: [Libguestfs] Build failure of libnbd
by Richard W.M. Jones
[Adding libguestfs mailing list]
I reproduced it locally - the difference was installing "gcc-go".
I only had golang-bin installed previously. With gcc-go installed:
../run go install libguestfs.org/libnbd
write of Go pointer 0xc000016060 to non-Go memory 0x7f5fe8297390
fatal error: Go pointer stored into non-Go memory
runtime stack:
runtime_mstart
../../../libgo/runtime/proc.c:593
Notice that these two packages both provide /usr/bin/go, using
alternatives:
$ rpm -qf /usr/bin/go
golang-bin-1.15.3-1.fc34.x86_64
gcc-go-10.2.1-5.fc34.x86_64
$ ll /usr/bin/go
lrwxrwxrwx. 1 root root 20 Oct 15 12:46 /usr/bin/go -> /etc/alternatives/go
So the easy fix is to remove gcc-go from your Dockerfile.
However I will take a look at why gcc-go doesn't work, as I guess we
ought to try to support both, or if gcc-go cannot work then we ought
to reject it in ./configure.
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html
4 years, 1 month
Why does libxml2 limit port numbers to 999,999,999?
by Richard W.M. Jones
The AF_VSOCK protocol added in Linux 5.6 uses a 32 bit port number.
For NBD we map this to simple URIs[1] like nbd+vsock://CID:PORT (where
CID is a number that acts a bit like a hostname and PORT is a 32 bit
port number). eg: nbd+vsock://1:1000000000/ would be port 10^9 on the
loopback address VMADDR_CID_LOCAL (== 1).
The problem is that libxml2 arbitrarily limits port numbers to
999,999,999. I don't see any support for this limit in RFC 3986 [2].
Here is the code:
https://github.com/GNOME/libxml2/blob/46837d47d59c7b8c9bd1d08a6a717a90a7f...
It doesn't even return an error, just truncates the port number. You
can see the problem with the program [3] below.
It seems like libxml2 chose to do this for convenience rather than
correctness. I think it should accept port numbers at least up to
signed int (the type used to store port numbers), and give an error if
the port number overflows.
Is there anything I'm missing or would a patch which implements that
be acceptable?
Also could the uri->port field be changed to unsigned int without
breaking ABI?
Rich.
[1] https://github.com/NetworkBlockDevice/nbd/blob/master/doc/uri.md
[2] https://tools.ietf.org/html/rfc3986#section-3.2.3
[3]
$ cat port.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <libxml/uri.h>
int
main (int argc, char *argv[])
{
xmlURIPtr uri = xmlParseURI (argv[1]);
if (!uri) {
fprintf (stderr, "xmlParseURI failed\n");
exit (EXIT_FAILURE);
}
printf ("%s => uri->port = %d\n", argv[1], uri->port);
exit (EXIT_SUCCESS);
}
$ ./port nbd+vsock://1:1000
nbd+vsock://1:1000 => uri->port = 1000
$ ./port nbd+vsock://1:100000
nbd+vsock://1:100000 => uri->port = 100000
$ ./port nbd+vsock://1:10000000
nbd+vsock://1:10000000 => uri->port = 10000000
$ ./port nbd+vsock://1:1000000000
nbd+vsock://1:1000000000 => uri->port = 99999999
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-top is 'top' for virtual machines. Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://people.redhat.com/~rjones/virt-top
4 years, 1 month
[PATCH nbdkit] common/include/tvdiff.h: Add formal specification.
by Richard W.M. Jones
This commit adds a formal specification of tvdiff_usec and a partial
specification of subtract_timeval. These may be proved using Frama-C.
The existing functions ignored overflow, but it is possible to call
the functions with parameters that will cause overflow. So to create
a formal specification I had to modify the functions to signal
overflow. Luckily GCC and Clang have convenient __builtin*s which
make this easy, but Frama-C did not know about the builtins so I had
to add axioms for them.
This required modifying the callers in nbdkit-rate-filter and
nbdkit-stats-filter for the new signature. Neither existing call site
cared about overflow so we ignore it.
A regular test is added although it will be skipped unless Frama-C is
available. Note this test can take a few seconds to run.
Note this does not remove test-tvdiff.c. Now we have formally proven
the functions to be correct there is no need to test them, but since
the test exists we may as well keep it.
---
common/include/Makefile.am | 8 ++-
common/include/test-proof.sh | 49 ++++++++++++++
common/include/test-tvdiff.c | 4 +-
common/include/tvdiff.h | 124 ++++++++++++++++++++++++++++++++---
filters/rate/bucket.c | 4 +-
filters/stats/stats.c | 6 +-
6 files changed, 177 insertions(+), 18 deletions(-)
diff --git a/common/include/Makefile.am b/common/include/Makefile.am
index a7d0d026..0521f65b 100644
--- a/common/include/Makefile.am
+++ b/common/include/Makefile.am
@@ -45,13 +45,14 @@ EXTRA_DIST = \
nextnonzero.h \
random.h \
rounding.h \
+ test-proof.sh \
tvdiff.h \
unix-path-max.h \
$(NULL)
# Unit tests.
-TESTS = \
+check_PROGRAMS = \
test-ascii-ctype \
test-ascii-string \
test-byte-swapping \
@@ -63,7 +64,10 @@ TESTS = \
test-random \
test-tvdiff \
$(NULL)
-check_PROGRAMS = $(TESTS)
+TESTS = \
+ test-proof.sh \
+ $(check_PROGRAMS) \
+ $(NULL)
test_ascii_ctype_SOURCES = test-ascii-ctype.c ascii-ctype.h
test_ascii_ctype_CPPFLAGS = -I$(srcdir)
diff --git a/common/include/test-proof.sh b/common/include/test-proof.sh
new file mode 100755
index 00000000..3d9fd424
--- /dev/null
+++ b/common/include/test-proof.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2020 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test formal specifications using frama-c.
+
+source ../../tests/functions.sh
+set -e
+set -x
+
+requires frama-c -version
+requires frama-c -wp-h
+
+sources="tvdiff.h"
+
+# For how to get Frama-C to exit with an error if the proof fails,
+# see: https://git.frama-c.com/pub/frama-c/-/issues/34
+frama-c -wp -wp-rte $sources \
+ -cpp-extra-args="-I../.. -DFRAMA_C=1" \
+ -wp-par=`nproc` \
+ -then -report-classify -report-status -report-unclassified-unknown ERROR
diff --git a/common/include/test-tvdiff.c b/common/include/test-tvdiff.c
index abefb2e7..1bf6523b 100644
--- a/common/include/test-tvdiff.c
+++ b/common/include/test-tvdiff.c
@@ -46,7 +46,9 @@
#define TEST_TVDIFF(tv1, tv2, expected) \
do { \
- int64_t actual = tvdiff_usec (&(tv1), &(tv2)); \
+ int64_t actual; \
+ \
+ tvdiff_usec (&(tv1), &(tv2), &actual); \
\
if (actual != (expected)) { \
fprintf (stderr, \
diff --git a/common/include/tvdiff.h b/common/include/tvdiff.h
index cec83458..7680ae30 100644
--- a/common/include/tvdiff.h
+++ b/common/include/tvdiff.h
@@ -37,28 +37,134 @@
#include <config.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/time.h>
-/* Return the number of µs (microseconds) in y - x. */
-static inline int64_t
-tvdiff_usec (const struct timeval *x, const struct timeval *y)
+/*@
+ predicate valid_timeval (struct timeval tv) =
+ tv.tv_sec >= 0 && tv.tv_usec >= 0 && tv.tv_usec < 1000000;
+ logic integer tv_to_microseconds (struct timeval tv) =
+ tv.tv_sec * 1000000 + tv.tv_usec;
+ */
+
+#ifdef FRAMA_C
+
+/*@
+ assigns *r;
+ behavior success:
+ assumes INT64_MIN <= a + b <= INT64_MAX;
+ ensures *r == a + b;
+ ensures \result == \false;
+ behavior overflow:
+ assumes !(INT64_MIN <= a + b <= INT64_MAX);
+ ensures \result == \true;
+ */
+extern bool __builtin_add_overflow (int64_t a, int64_t b, int64_t *r);
+/*@
+ assigns *r;
+ behavior success:
+ assumes INT64_MIN <= a - b <= INT64_MAX;
+ ensures *r == a - b;
+ ensures \result == \false;
+ behavior overflow:
+ assumes !(INT64_MIN <= a - b <= INT64_MAX);
+ ensures \result == \true;
+ */
+extern bool __builtin_sub_overflow (int64_t a, int64_t b, int64_t *r);
+/*@
+ assigns *r;
+ behavior success:
+ assumes INT64_MIN <= a * b <= INT64_MAX;
+ ensures *r == a * b;
+ ensures \result == \false;
+ behavior overflow:
+ assumes !(INT64_MIN <= a * b <= INT64_MAX);
+ ensures \result == \true;
+ */
+extern bool __builtin_mul_overflow (int64_t a, int64_t b, int64_t *r);
+
+#endif /* FRAMA_C */
+
+/* Return the number of µs (microseconds) *r = *y - *x.
+ * On overflow, returns -1.
+ */
+/*@
+ requires \valid_read (x) && \valid_read (y);
+ requires valid_timeval (*x) && valid_timeval (*y);
+ requires \valid (r);
+ assigns *r;
+ behavior success:
+ assumes INT64_MIN <= tv_to_microseconds (*y) - tv_to_microseconds (*x)
+ <= INT64_MAX;
+ ensures \result == 0;
+ ensures *r == tv_to_microseconds (*y) - tv_to_microseconds (*x);
+ behavior failure:
+ assumes !(INT64_MIN <= tv_to_microseconds (*y) - tv_to_microseconds (*x)
+ <= INT64_MAX);
+ ensures \result == -1;
+ ensures *r == 0;
+ complete behaviors;
+ disjoint behaviors;
+ */
+static inline int
+tvdiff_usec (const struct timeval *x, const struct timeval *y,
+ int64_t *r)
{
int64_t usec;
- usec = (y->tv_sec - x->tv_sec) * 1000000;
- usec += y->tv_usec - x->tv_usec;
- return usec;
+ if (__builtin_sub_overflow (y->tv_sec, x->tv_sec, &usec)) {
+ overflow:
+ *r = 0;
+ return -1;
+ }
+ if (__builtin_mul_overflow (usec, 1000000, &usec))
+ goto overflow;
+ if (__builtin_add_overflow (usec, y->tv_usec - x->tv_usec, &usec))
+ goto overflow;
+
+ *r = usec;
+ return 0;
}
-/* Return timeval difference as another struct timeval. z = y - x. */
-static inline void
+/* Return timeval difference as another struct timeval z = y - x.
+ * On overflow, returns -1.
+ */
+/*@
+ requires \valid_read (x) && \valid_read (y);
+ requires valid_timeval (*x) && valid_timeval (*y);
+ requires \valid (z);
+ assigns *z;
+ behavior success:
+ assumes INT64_MIN <= tv_to_microseconds (*y) - tv_to_microseconds (*x)
+ <= INT64_MAX;
+ ensures \result == 0;
+ // XXX alt-ergo 2.2.0 cannot prove this:
+ // ensures tv_to_microseconds (*z) ==
+ // tv_to_microseconds (*y) - tv_to_microseconds (*x);
+ // note that the result is not a valid_timeval because
+ // z->tv_sec could be negative
+ behavior failure:
+ assumes !(INT64_MIN <= tv_to_microseconds (*y) - tv_to_microseconds (*x)
+ <= INT64_MAX);
+ ensures \result == -1;
+ ensures z->tv_sec == 0 && z->tv_usec == 0;
+ complete behaviors;
+ disjoint behaviors;
+ */
+static inline int
subtract_timeval (const struct timeval *x, const struct timeval *y,
struct timeval *z)
{
- int64_t usec = tvdiff_usec (x, y);
+ int64_t usec;
+ if (tvdiff_usec (x, y, &usec) == -1) {
+ z->tv_sec = z->tv_usec = 0;
+ return -1;
+ }
z->tv_sec = usec / 1000000;
z->tv_usec = usec % 1000000;
+ return 0;
}
#endif /* NBDKIT_TVDIFF_H */
diff --git a/filters/rate/bucket.c b/filters/rate/bucket.c
index b3addac6..2accb810 100644
--- a/filters/rate/bucket.c
+++ b/filters/rate/bucket.c
@@ -127,9 +127,7 @@ bucket_run (struct bucket *bucket, uint64_t n, struct timespec *ts)
/* Work out how much time has elapsed since we last added tokens to
* the bucket, and add the correct number of tokens.
*/
- usec = tvdiff_usec (&bucket->tv, &now);
- if (usec < 0) /* Maybe happens if system time not monotonic? */
- usec = 0;
+ tvdiff_usec (&bucket->tv, &now, &usec);
add = bucket->rate * usec / 1000000;
add = MIN (add, bucket->capacity - bucket->level);
diff --git a/filters/stats/stats.c b/filters/stats/stats.c
index 639ceacf..3b633cdf 100644
--- a/filters/stats/stats.c
+++ b/filters/stats/stats.c
@@ -164,7 +164,7 @@ stats_unload (void)
int64_t usecs;
gettimeofday (&now, NULL);
- usecs = tvdiff_usec (&start_t, &now);
+ tvdiff_usec (&start_t, &now, &usecs);
if (fp && usecs > 0) {
ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
print_stats (usecs);
@@ -247,10 +247,10 @@ static inline void
record_stat (nbdstat *st, uint32_t count, const struct timeval *start)
{
struct timeval end;
- uint64_t usecs;
+ int64_t usecs;
gettimeofday (&end, NULL);
- usecs = tvdiff_usec (start, &end);
+ tvdiff_usec (start, &end, &usecs);
ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
st->ops++;
--
2.29.0.rc2
4 years, 1 month
[libnbd PATCH] info: Keep request within 4G bound
by Eric Blake
Otherwise, we get a failure due to Numerical result out of range.
And for safety's sake, we are best capping our request to an aligned
value, if the server insists on minimum alignment.
Fixes: f3fd935c
---
info/nbdinfo.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/info/nbdinfo.c b/info/nbdinfo.c
index 1afdf98..2b22f51 100644
--- a/info/nbdinfo.c
+++ b/info/nbdinfo.c
@@ -31,6 +31,8 @@
#include <libnbd.h>
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
static const char *progname;
static FILE *fp;
static bool list_all = false;
@@ -267,7 +269,7 @@ main (int argc, char *argv[])
fprintf (fp, "%" PRIi64 "\n", size);
}
else if (map) { /* --map (!list_all) */
- uint64_t offset, prev_offset;
+ uint64_t offset, prev_offset, align, max_len;
/* Did we get the requested map? */
if (!nbd_can_meta_context (nbd, map)) {
@@ -276,6 +278,8 @@ main (int argc, char *argv[])
progname, map);
exit (EXIT_FAILURE);
}
+ align = nbd_get_block_size (nbd, LIBNBD_SIZE_MINIMUM) ?: 512;
+ max_len = UINT32_MAX - align + 1;
size = nbd_get_size (nbd);
if (size == -1) {
@@ -286,7 +290,7 @@ main (int argc, char *argv[])
if (json_output) fprintf (fp, "[\n");
for (offset = 0; offset < size;) {
prev_offset = offset;
- if (nbd_block_status (nbd, size - offset, offset,
+ if (nbd_block_status (nbd, MIN (size - offset, max_len), offset,
(nbd_extent_callback) { .callback = extent_callback,
.user_data = &offset },
0) == -1) {
--
2.29.0.rc1
4 years, 1 month
[PATCH common v2 0/4] Windows BitLocker support.
by Richard W.M. Jones
For links to the original patch series, see:
https://bugzilla.redhat.com/show_bug.cgi?id=1808977#c8
The original feedback was that ignoring errors from guestfs_luks_uuid
would ignore legitimate errors from non-BitLocker disks, so I have
modified this series so that errors are only ignored in the BitLocker
case. As noted in the 4th patch there is no actual error in the
BitLocker case, cryptsetup luksUUID simply exits without printing
anything.
Rich.
4 years, 1 month
[PATCH libnbd] info: Write output atomically.
by Richard W.M. Jones
If the server fails, nbdinfo can write partial output before the error
message (albeit on different channels). Here is an example:
$ nbdkit eval open='echo EIO fail >&2; exit 1' --run 'nbdinfo --json "$uri"'
{
"protocol": "newstyle-fixed",
"TLS": false,
nbdkit: eval[1]: error: /tmp/nbdkitii3pZW/open: fail
nbdkit: eval[1]: error: /tmp/nbdkitii3pZW/open: fail
nbdinfo: nbd_opt_go: server replied with error to opt_go request: No such file or directory
Notice the partial JSON document, and the error message from nbdinfo.
With this commit nbdinfo tries to produce either the complete output
or the error message.
---
info/Makefile.am | 1 +
info/info-atomic-output.sh | 32 ++++++
info/nbdinfo.c | 194 ++++++++++++++++++++++---------------
3 files changed, 147 insertions(+), 80 deletions(-)
diff --git a/info/Makefile.am b/info/Makefile.am
index 9557f59..f0edec6 100644
--- a/info/Makefile.am
+++ b/info/Makefile.am
@@ -32,6 +32,7 @@ info_sh_files = \
info-map-base-allocation.sh \
info-map-base-allocation-json.sh \
info-map-qemu-dirty-bitmap.sh \
+ info-atomic-output.sh \
$(NULL)
EXTRA_DIST = \
diff --git a/info/info-atomic-output.sh b/info/info-atomic-output.sh
new file mode 100755
index 0000000..11b2ab3
--- /dev/null
+++ b/info/info-atomic-output.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# nbd client library in userspace
+# Copyright (C) 2020 Red Hat Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+. ../tests/functions.sh
+
+set -e
+set -x
+
+requires nbdkit --version
+requires nbdkit eval --version
+
+out=info-atomic-output.out
+cleanup_fn rm -f $out
+
+nbdkit -U - eval open='echo EIO fail >&2; exit 1' \
+ --run '$VG nbdinfo --size "nbd+unix:///?socket=$unixsocket"' > $out ||:
+test ! -s $out
diff --git a/info/nbdinfo.c b/info/nbdinfo.c
index 31e2508..24ec129 100644
--- a/info/nbdinfo.c
+++ b/info/nbdinfo.c
@@ -32,6 +32,7 @@
#include <libnbd.h>
static const char *progname;
+static FILE *fp;
static bool list_all = false;
static bool probe_content, content_flag, no_content_flag;
static bool json_output = false;
@@ -121,6 +122,8 @@ main (int argc, char *argv[])
int64_t size;
const char *protocol;
int tls_negotiated;
+ char *output = NULL;
+ size_t output_len = 0;
progname = argv[0];
@@ -207,6 +210,17 @@ main (int argc, char *argv[])
if (map)
probe_content = false;
+ /* Try to write output atomically. We spool output into a
+ * memstream, pointed to by fp, and write it all at once at the end.
+ * On error nothing should be printed on stdout.
+ */
+ fp = open_memstream (&output, &output_len);
+ if (fp == NULL) {
+ fprintf (stderr, "%s: ", progname);
+ perror ("open_memstream");
+ exit (EXIT_FAILURE);
+ }
+
/* Open the NBD side. */
nbd = nbd_create ();
if (nbd == NULL) {
@@ -250,7 +264,7 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
- printf ("%" PRIi64 "\n", size);
+ fprintf (fp, "%" PRIi64 "\n", size);
}
else if (map) { /* --map (!list_all) */
uint64_t offset, prev_offset;
@@ -269,7 +283,7 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
- if (json_output) printf ("[\n");
+ if (json_output) fprintf (fp, "[\n");
for (offset = 0; offset < size;) {
prev_offset = offset;
if (nbd_block_status (nbd, size - offset, offset,
@@ -288,7 +302,7 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
}
- if (json_output) printf ("\n]\n");
+ if (json_output) fprintf (fp, "\n]\n");
}
else { /* not --size or --map */
/* Print per-connection fields. */
@@ -297,22 +311,22 @@ main (int argc, char *argv[])
if (!json_output) {
if (protocol) {
- printf ("protocol: %s", protocol);
+ fprintf (fp, "protocol: %s", protocol);
if (tls_negotiated >= 0)
- printf (" %s TLS", tls_negotiated ? "with" : "without");
- printf ("\n");
+ fprintf (fp, " %s TLS", tls_negotiated ? "with" : "without");
+ fprintf (fp, "\n");
}
}
else {
- printf ("{\n");
+ fprintf (fp, "{\n");
if (protocol) {
- printf ("\"protocol\": ");
+ fprintf (fp, "\"protocol\": ");
print_json_string (protocol);
- printf (",\n");
+ fprintf (fp, ",\n");
}
if (tls_negotiated >= 0)
- printf ("\"TLS\": %s,\n", tls_negotiated ? "true" : "false");
+ fprintf (fp, "\"TLS\": %s,\n", tls_negotiated ? "true" : "false");
}
if (!list_all)
@@ -321,7 +335,7 @@ main (int argc, char *argv[])
list_all_exports (nbd, argv[optind]);
if (json_output)
- printf ("}\n");
+ fprintf (fp, "}\n");
}
for (i = 0; i < export_list.len; i++) {
@@ -333,6 +347,19 @@ main (int argc, char *argv[])
nbd_opt_abort (nbd);
nbd_shutdown (nbd, 0);
nbd_close (nbd);
+
+ /* Close the output stream and copy it to the real stdout. */
+ if (fclose (fp) == EOF) {
+ fprintf (stderr, "%s: ", progname);
+ perror ("fclose");
+ exit (EXIT_FAILURE);
+ }
+ if (puts (output) == EOF) {
+ fprintf (stderr, "%s: ", progname);
+ perror ("puts");
+ exit (EXIT_FAILURE);
+ }
+
exit (EXIT_SUCCESS);
}
@@ -451,126 +478,133 @@ list_one_export (struct nbd_handle *nbd, const char *desc,
content = get_content (nbd, size);
if (!json_output) {
- printf ("export=");
+ fprintf (fp, "export=");
/* Might as well use the JSON function to get an escaped string here ... */
print_json_string (export_name);
- printf (":\n");
+ fprintf (fp, ":\n");
if (desc && *desc)
- printf ("\tdescription: %s\n", desc);
- printf ("\texport-size: %" PRIi64 "\n", size);
+ fprintf (fp, "\tdescription: %s\n", desc);
+ fprintf (fp, "\texport-size: %" PRIi64 "\n", size);
if (content)
- printf ("\tcontent: %s\n", content);
+ fprintf (fp, "\tcontent: %s\n", content);
if (show_context) {
- printf ("\tcontexts:\n");
+ fprintf (fp, "\tcontexts:\n");
for (struct context_list *next = contexts; next; next = next->next)
- printf ("\t\t%s\n", next->name);
+ fprintf (fp, "\t\t%s\n", next->name);
}
if (is_rotational >= 0)
- printf ("\t%s: %s\n", "is_rotational", is_rotational ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "is_rotational",
+ is_rotational ? "true" : "false");
if (is_read_only >= 0)
- printf ("\t%s: %s\n", "is_read_only", is_read_only ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "is_read_only",
+ is_read_only ? "true" : "false");
if (can_cache >= 0)
- printf ("\t%s: %s\n", "can_cache", can_cache ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_cache", can_cache ? "true" : "false");
if (can_df >= 0)
- printf ("\t%s: %s\n", "can_df", can_df ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_df", can_df ? "true" : "false");
if (can_fast_zero >= 0)
- printf ("\t%s: %s\n", "can_fast_zero", can_fast_zero ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_fast_zero",
+ can_fast_zero ? "true" : "false");
if (can_flush >= 0)
- printf ("\t%s: %s\n", "can_flush", can_flush ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_flush", can_flush ? "true" : "false");
if (can_fua >= 0)
- printf ("\t%s: %s\n", "can_fua", can_fua ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_fua", can_fua ? "true" : "false");
if (can_multi_conn >= 0)
- printf ("\t%s: %s\n", "can_multi_conn", can_multi_conn ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_multi_conn",
+ can_multi_conn ? "true" : "false");
if (can_trim >= 0)
- printf ("\t%s: %s\n", "can_trim", can_trim ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_trim", can_trim ? "true" : "false");
if (can_zero >= 0)
- printf ("\t%s: %s\n", "can_zero", can_zero ? "true" : "false");
+ fprintf (fp, "\t%s: %s\n", "can_zero", can_zero ? "true" : "false");
if (block_minimum > 0)
- printf ("\t%s: %" PRId64 "\n", "block_size_minimum", block_minimum);
+ fprintf (fp, "\t%s: %" PRId64 "\n", "block_size_minimum", block_minimum);
if (block_preferred > 0)
- printf ("\t%s: %" PRId64 "\n", "block_size_preferred", block_preferred);
+ fprintf (fp, "\t%s: %" PRId64 "\n", "block_size_preferred",
+ block_preferred);
if (block_maximum > 0)
- printf ("\t%s: %" PRId64 "\n", "block_size_maximum", block_maximum);
+ fprintf (fp, "\t%s: %" PRId64 "\n", "block_size_maximum", block_maximum);
}
else {
if (first)
- printf ("\"exports\": [\n");
- printf ("\t{\n");
+ fprintf (fp, "\"exports\": [\n");
+ fprintf (fp, "\t{\n");
- printf ("\t\"export-name\": ");
+ fprintf (fp, "\t\"export-name\": ");
print_json_string (export_name);
- printf (",\n");
+ fprintf (fp, ",\n");
if (desc && *desc) {
- printf ("\t\"description\": ");
+ fprintf (fp, "\t\"description\": ");
print_json_string (desc);
- printf (",\n");
+ fprintf (fp, ",\n");
}
if (content) {
- printf ("\t\"content\": ");
+ fprintf (fp, "\t\"content\": ");
print_json_string (content);
- printf (",\n");
+ fprintf (fp, ",\n");
}
if (show_context) {
- printf ("\t\"contexts\": [\n");
+ fprintf (fp, "\t\"contexts\": [\n");
for (struct context_list *next = contexts; next; next = next->next) {
- printf ("\t\t");
+ fprintf (fp, "\t\t");
print_json_string (next->name);
if (next->next)
- putchar(',');
- putchar('\n');
+ fputc (',', fp);
+ fputc ('\n', fp);
}
- printf ("\t],\n");
+ fprintf (fp, "\t],\n");
}
if (is_rotational >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"is_rotational", is_rotational ? "true" : "false");
if (is_read_only >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"is_read_only", is_read_only ? "true" : "false");
if (can_cache >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_cache", can_cache ? "true" : "false");
if (can_df >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_df", can_df ? "true" : "false");
if (can_fast_zero >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_fast_zero", can_fast_zero ? "true" : "false");
if (can_flush >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_flush", can_flush ? "true" : "false");
if (can_fua >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_fua", can_fua ? "true" : "false");
if (can_multi_conn >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_multi_conn", can_multi_conn ? "true" : "false");
if (can_trim >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_trim", can_trim ? "true" : "false");
if (can_zero >= 0)
- printf ("\t\"%s\": %s,\n",
+ fprintf (fp, "\t\"%s\": %s,\n",
"can_zero", can_zero ? "true" : "false");
if (block_minimum > 0)
- printf ("\t\"%s\": %" PRId64 ",\n", "block_size_minimum", block_minimum);
+ fprintf (fp, "\t\"%s\": %" PRId64 ",\n", "block_size_minimum",
+ block_minimum);
if (block_preferred > 0)
- printf ("\t\"%s\": %" PRId64 ",\n", "block_size_preferred",
+ fprintf (fp, "\t\"%s\": %" PRId64 ",\n", "block_size_preferred",
block_preferred);
if (block_maximum > 0)
- printf ("\t\"%s\": %" PRId64 ",\n", "block_size_maximum", block_maximum);
+ fprintf (fp, "\t\"%s\": %" PRId64 ",\n", "block_size_maximum",
+ block_maximum);
/* Put this one at the end because of the stupid comma thing in JSON. */
- printf ("\t\"export-size\": %" PRIi64 "\n", size);
+ fprintf (fp, "\t\"export-size\": %" PRIi64 "\n", size);
if (last)
- printf ("\t} ]\n");
+ fprintf (fp, "\t} ]\n");
else
- printf ("\t},\n");
+ fprintf (fp, "\t},\n");
}
while (contexts) {
@@ -590,7 +624,7 @@ list_all_exports (struct nbd_handle *nbd1, const char *uri)
size_t i;
if (export_list.len == 0 && json_output)
- printf ("\"exports\": []\n");
+ fprintf (fp, "\"exports\": []\n");
for (i = 0; i < export_list.len; ++i) {
const char *name = export_list.names[i];
@@ -635,22 +669,22 @@ list_all_exports (struct nbd_handle *nbd1, const char *uri)
static void
print_json_string (const char *s)
{
- putc ('"', stdout);
+ fputc ('"', fp);
for (; *s; s++) {
switch (*s) {
case '\\':
case '"':
- putc ('\\', stdout);
- putc (*s, stdout);
+ fputc ('\\', fp);
+ fputc (*s, fp);
break;
default:
if (*s < ' ')
- printf ("\\u%04x", *s);
+ fprintf (fp, "\\u%04x", *s);
else
- putc (*s, stdout);
+ fputc (*s, fp);
}
}
- putc ('"', stdout);
+ fputc ('"', fp);
}
/* Run the file(1) command on the first part of the export and save
@@ -768,27 +802,27 @@ extent_callback (void *user_data, const char *metacontext,
const char *descr = extent_description (map, entries[i+1]);
if (!json_output) {
- printf ("%10" PRIu64 " "
- "%10" PRIu32 " "
- "%3" PRIu32,
- offset, entries[i], entries[i+1]);
+ fprintf (fp, "%10" PRIu64 " "
+ "%10" PRIu32 " "
+ "%3" PRIu32,
+ offset, entries[i], entries[i+1]);
if (descr)
- printf (" %s", descr);
- printf ("\n");
+ fprintf (fp, " %s", descr);
+ fprintf (fp, "\n");
}
else {
if (comma)
- printf (",\n");
+ fprintf (fp, ",\n");
- printf ("{ \"offset\": %" PRIu64 ", "
- "\"length\": %" PRIu32 ", "
- "\"type\": %" PRIu32,
- offset, entries[i], entries[i+1]);
+ fprintf (fp, "{ \"offset\": %" PRIu64 ", "
+ "\"length\": %" PRIu32 ", "
+ "\"type\": %" PRIu32,
+ offset, entries[i], entries[i+1]);
if (descr) {
- printf (", \"description\": ");
+ fprintf (fp, ", \"description\": ");
print_json_string (descr);
}
- printf ("}");
+ fprintf (fp, "}");
comma = true;
}
--
2.27.0
4 years, 1 month
Debugging libguestfs
by Alexander Prada
Here is my output I am getting on my kali machine. I believe that the error is:
guestfsd: error while loading shared libraries: libtsk.so.19: cannot open shared object file: No such file or directory
I tried to change the LD_LIBRARY_PATH to numerous locations and libtsk.so.19 is inside /usr/lib/x86_64-linux-gnu
4 years, 1 month