Richard W.M. Jones
2012-Jul-09 15:13 UTC
[Libguestfs] [PATCH 0/4] Provide guestmount --pid-file and document possible race when unmounting FUSE filesystems.
The full description of this bug is here: https://bugzilla.redhat.com/show_bug.cgi?id=838592 and the effect it has on OpenStack is described here: https://bugzilla.redhat.com/show_bug.cgi?id=835466#c9 Rich.
Richard W.M. Jones
2012-Jul-09 15:13 UTC
[Libguestfs] [PATCH 1/4] fuse: Document race condition possible with fusermount.
From: "Richard W.M. Jones" <rjones at redhat.com> See also: https://bugzilla.redhat.com/show_bug.cgi?id=835466#c9 --- fuse/guestmount.pod | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fuse/guestmount.pod b/fuse/guestmount.pod index a0bc6f1..c37301c 100644 --- a/fuse/guestmount.pod +++ b/fuse/guestmount.pod @@ -106,6 +106,22 @@ namespace using the Linux-specific L<clone(2)>/L<unshare(2)> flag C<CLONE_NEWNS>. Unfortunately at the moment this requires root and we would also probably need to add it as a feature to guestmount. +=head2 Race conditions possible when shutting down the connection + +When C<fusermount -u> exits, guestmount may still be running and +cleaning up the mountpoint. The disk image will not be fully +finalized. + +This means that scripts like the following have a nasty race +condition: + + guestmount -a disk.img -i /mnt + # copy things into /mnt + fusermount -u /mnt + # immediately try to use 'disk.img' <-- UNSAFE + +The solution is to spin waiting for the guestmount process to exit. + =head1 OPTIONS =over 4 -- 1.7.10.4
Richard W.M. Jones
2012-Jul-09 15:13 UTC
[Libguestfs] [PATCH 2/4] fuse: Link to mount-local documentation in guestmount man page.
From: "Richard W.M. Jones" <rjones at redhat.com> --- fuse/guestmount.pod | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fuse/guestmount.pod b/fuse/guestmount.pod index c37301c..d459045 100644 --- a/fuse/guestmount.pod +++ b/fuse/guestmount.pod @@ -122,6 +122,10 @@ condition: The solution is to spin waiting for the guestmount process to exit. +Note that if you use the C<guestfs_mount_local> API directly (see +L<guestfs(3)/MOUNT LOCAL>) then it is much easier to write a safe, +race-free program. + =head1 OPTIONS =over 4 @@ -354,7 +358,7 @@ L<virt-inspector(1)>, L<virt-cat(1)>, L<virt-edit(1)>, L<virt-tar(1)>, -L<guestfs(3)>, +L<guestfs(3)/MOUNT LOCAL>, L<http://libguestfs.org/>, L<http://fuse.sf.net/>. -- 1.7.10.4
Richard W.M. Jones
2012-Jul-09 15:13 UTC
[Libguestfs] [PATCH 3/4] fuse: Add guestmount --pid-file option (RHBZ#838592).
From: "Richard W.M. Jones" <rjones at redhat.com>
---
fuse/guestmount.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-
fuse/guestmount.pod | 8 +++++++-
2 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/fuse/guestmount.c b/fuse/guestmount.c
index 0c2e2dc..fd31726 100644
--- a/fuse/guestmount.c
+++ b/fuse/guestmount.c
@@ -42,6 +42,8 @@
#include "guestmount.h"
#include "options.h"
+static int write_pid_file (const char *pid_file, pid_t pid);
+
#ifndef HAVE_FUSE_OPT_ADD_OPT_ESCAPED
/* Copied from lib/fuse_opt.c and modified.
* Copyright (C) 2001-2007 Miklos Szeredi <miklos at szeredi.hu>
@@ -119,6 +121,7 @@ usage (int status)
" -m|--mount dev[:mnt[:opts]] Mount dev on mnt (if omitted,
/)\n"
" -n|--no-sync Don't autosync\n"
" -o|--option opt Pass extra option to FUSE\n"
+ " --pid-file filename Write PID to filename\n"
" -r|--ro Mount read-only\n"
" --selinux Enable SELinux support\n"
" -v|--verbose Verbose messages\n"
@@ -161,6 +164,7 @@ main (int argc, char *argv[])
{ "mount", 1, 0, 'm' },
{ "no-sync", 0, 0, 'n' },
{ "option", 1, 0, 'o' },
+ { "pid-file", 1, 0, 0 },
{ "ro", 0, 0, 'r' },
{ "rw", 0, 0, 'w' },
{ "selinux", 0, 0, 0 },
@@ -184,6 +188,7 @@ main (int argc, char *argv[])
int dir_cache_timeout = -1;
int do_fork = 1;
char *fuse_options = NULL;
+ char *pid_file = NULL;
struct guestfs_mount_local_argv optargs;
@@ -227,6 +232,8 @@ main (int argc, char *argv[])
echo_keys = 1;
} else if (STREQ (long_options[option_index].name, "live")) {
live = 1;
+ } else if (STREQ (long_options[option_index].name, "pid-file"))
{
+ pid_file = optarg;
} else {
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
program_name, long_options[option_index].name, option_index);
@@ -406,8 +413,12 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
- if (pid != 0) /* parent */
+ if (pid != 0) { /* parent */
+ if (write_pid_file (pid_file, pid) == -1)
+ _exit (EXIT_FAILURE);
+
_exit (EXIT_SUCCESS);
+ }
/* Emulate what old fuse_daemonize used to do. */
if (setsid () == -1) {
@@ -426,6 +437,11 @@ main (int argc, char *argv[])
close (fd);
}
}
+ else {
+ /* not forking, write PID file anyway */
+ if (write_pid_file (pid_file, getpid ()) == -1)
+ exit (EXIT_FAILURE);
+ }
/* Main loop. */
r = guestfs_mount_local_run (g);
@@ -435,5 +451,33 @@ main (int argc, char *argv[])
r = -1;
guestfs_close (g);
+ /* Don't delete PID file until the cleanup has been completed. */
+ if (pid_file)
+ unlink (pid_file);
+
exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
+
+static int
+write_pid_file (const char *pid_file, pid_t pid)
+{
+ FILE *fp;
+
+ if (pid_file == NULL)
+ return 0;
+
+ fp = fopen (pid_file, "w");
+ if (fp == NULL) {
+ error:
+ perror (pid_file);
+ return -1;
+ }
+
+ if (fprintf (fp, "%d\n", pid) == -1)
+ goto error;
+
+ if (fclose (fp) == -1)
+ goto error;
+
+ return 0;
+}
diff --git a/fuse/guestmount.pod b/fuse/guestmount.pod
index d459045..fdcf133 100644
--- a/fuse/guestmount.pod
+++ b/fuse/guestmount.pod
@@ -120,7 +120,9 @@ condition:
fusermount -u /mnt
# immediately try to use 'disk.img' <-- UNSAFE
-The solution is to spin waiting for the guestmount process to exit.
+The solution is to use the I<--pid-file> option to write the
+guestmount PID to a file, then after fusermount spin waiting for this
+PID to exit.
Note that if you use the C<guestfs_mount_local> API directly (see
L<guestfs(3)/MOUNT LOCAL>) then it is much easier to write a safe,
@@ -282,6 +284,10 @@ to the chosen values.
=back
+=item B<--pid-file filename>
+
+Write the PID of the guestmount worker process to C<filename>.
+
=item B<-r>
=item B<--ro>
--
1.7.10.4
Richard W.M. Jones
2012-Jul-09 15:13 UTC
[Libguestfs] [PATCH 4/4] fuse: Add regression test for RHBZ#838592.
From: "Richard W.M. Jones" <rjones at redhat.com> --- fuse/Makefile.am | 2 +- fuse/test-fuse-umount-race.sh | 77 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100755 fuse/test-fuse-umount-race.sh diff --git a/fuse/Makefile.am b/fuse/Makefile.am index 1ab8c7b..8176221 100644 --- a/fuse/Makefile.am +++ b/fuse/Makefile.am @@ -73,7 +73,7 @@ stamp-guestmount.pod: guestmount.pod # Tests. if ENABLE_APPLIANCE -TESTS = test-fuse.sh +TESTS = test-fuse.sh test-fuse-umount-race.sh endif ENABLE_APPLIANCE TESTS_ENVIRONMENT = \ top_builddir=.. \ diff --git a/fuse/test-fuse-umount-race.sh b/fuse/test-fuse-umount-race.sh new file mode 100755 index 0000000..a5a6e21 --- /dev/null +++ b/fuse/test-fuse-umount-race.sh @@ -0,0 +1,77 @@ +#!/bin/bash - +# libguestfs +# Copyright (C) 2012 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# https://bugzilla.redhat.com/show_bug.cgi?id=838592 +# This tests that the --pid-file option can be used to fix the race. + +unset CDPATH +set -e +#set -v + +if [ -n "$SKIP_TEST_FUSE_SH" ]; then + echo "$0: test skipped because environment variable is set." + exit 0 +fi + +if [ ! -w /dev/fuse ]; then + echo "SKIPPING guestmount test, because there is no /dev/fuse." + exit 0 +fi + +rm -f test.qcow2 test-copy.qcow2 test.pid +rm -rf mp + +# Make a copy of the Fedora image so we can write to it then discard it. +qemu-img create -F raw -b ../tests/guests/fedora.img -f qcow2 test.qcow2 + +mkdir mp +./guestmount -a test.qcow2 -m /dev/VG/Root --pid-file test.pid mp +cp $0 mp/test-umount + +count=10 +while ! fusermount -u mp && [ $count -gt 0 ]; do + sleep 1 + ((count--)) +done +if [ $count -eq 0 ]; then + echo "$0: fusermount failed after 10 attempts" + exit 1 +fi + +# Wait for guestmount to exit. +count=10 +while kill -0 `cat test.pid` 2>/dev/null && [ $count -gt 0 ]; do + sleep 1 + ((count--)) +done +if [ $count -eq 0 ]; then + echo "$0: wait for guestmount to exit failed after 10 seconds" + exit 1 +fi + +# It should now be safe to copy and read the disk image. +cp test.qcow2 test-copy.qcow2 + +if [ "$(../fish/guestfish -a test-copy.qcow2 --ro -i is-file /test-umount)" != "true" ]; then + echo "$0: test failed" + exit 1 +fi + +rm test.qcow2 test-copy.qcow2 +rm -f test.pid +rm -r mp -- 1.7.10.4
Maybe Matching Threads
- [PATCH] fuse: Add guestmount-cleanup program to handle unmounting (RHBZ#916780).
- [PATCH v2] fuse: Add guestunmount program to handle unmounting (RHBZ#916780)
- [PATCH] fix fuse_opt_add_opt_escaped return type
- Guestmount seems not to sync changes
- [PATCH v2] New APIs: mount-local and umount-local using FUSE