[libvirt] [PATCH] Add support for probing filesystem with libblkid

From: "Daniel P. Berrange" <berrange@redhat.com> The LXC code for mounting container filesystems from block devices tries all filesystems in /etc/filesystems and possibly those in /proc/filesystems. The regular mount binary, however, first tries using libblkid to detect the format. Add support for doing the same in libvirt, since Fedora's /etc/filesystems is missing many formats, most notably ext4 which is the default filesystem Fedora uses! * src/Makefile.am: Link libvirt_lxc to libblkid * src/lxc/lxc_container.c: Probe filesystem format with liblkid --- src/Makefile.am | 4 ++ src/lxc/lxc_container.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index c36664b..bf26b19 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1495,6 +1495,10 @@ libvirt_lxc_CFLAGS = \ $(AUDIT_CFLAGS) \ -I@top_srcdir@/src/conf \ $(AM_CFLAGS) +if HAVE_LIBBLKID +libvirt_lxc_CFLAGS += $(BLKID_CFLAGS) +libvirt_lxc_LDADD += $(BLKID_LIBS) +endif endif endif EXTRA_DIST += $(LXC_CONTROLLER_SOURCES) diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index 36ac3a9..647aaea 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -47,6 +47,10 @@ # include <cap-ng.h> #endif +#if HAVE_LIBBLKID +# include <blkid/blkid.h> +#endif + #include "virterror_internal.h" #include "logging.h" #include "lxc_container.h" @@ -627,6 +631,86 @@ cleanup: } +#ifdef HAVE_LIBBLKID +static int +lxcContainerMountDetectFilesystem(const char *src, char **type) +{ + int fd; + int ret = -1; + int rc; + const char *data = NULL; + blkid_probe blkid = NULL; + + *type = NULL; + + if ((fd = open(src, O_RDONLY)) < 0) { + virReportSystemError(errno, + _("Unable to open filesystem %s"), src); + return -1; + } + + if (!(blkid = blkid_new_probe())) { + virReportSystemError(errno, "%s", + _("Unable to create blkid library handle")); + goto cleanup; + } + if (blkid_probe_set_device(blkid, fd, 0, 0) < 0) { + virReportSystemError(EINVAL, + _("Unable to associated device %s with blkid library"), + src); + goto cleanup; + } + + blkid_probe_enable_superblocks(blkid, 1); + + blkid_probe_set_superblocks_flags(blkid, BLKID_SUBLKS_TYPE); + + rc = blkid_do_safeprobe(blkid); + if (rc != 0) { + if (rc == 1) /* Nothing found, return success with *type == NULL */ + goto done; + + if (rc == -2) { + virReportSystemError(EINVAL, + _("Too many filesystems detected for %s"), + src); + } else { + virReportSystemError(errno, + _("Unable to detect filesystem for %s"), + src); + } + goto cleanup; + } + + if (blkid_probe_lookup_value(blkid, "TYPE", &data, NULL) < 0) { + virReportSystemError(ENOENT, + _("Unable to find filesystem type for %s"), + src); + goto cleanup; + } + + if (!(*type = strdup(data))) { + virReportOOMError(); + goto cleanup; + } + +done: + ret = 0; +cleanup: + VIR_FORCE_CLOSE(fd); + blkid_free_probe(blkid); + return ret; +} +#else /* ! HAVE_LIBBLKID */ +static int +lxcContainerMountDetectFilesystem(const char *src ATTRIBUTE_UNUSED, + char **type) +{ + /* No libblkid, so just return success with no detected type */ + *type = NULL; + return 0; +} +#endif /* ! HAVE_LIBBLKID */ /* * This functions attempts to do automatic detection of filesystem @@ -771,6 +855,8 @@ static int lxcContainerMountFSBlockHelper(virDomainFSDefPtr fs, { int fsflags = 0; int ret = -1; + char *format = NULL; + if (fs->readonly) fsflags |= MS_RDONLY; @@ -781,7 +867,21 @@ static int lxcContainerMountFSBlockHelper(virDomainFSDefPtr fs, goto cleanup; } - ret = lxcContainerMountFSBlockAuto(fs, fsflags, src, srcprefix); + if (lxcContainerMountDetectFilesystem(src, &format) < 0) + goto cleanup; + + if (format) { + VIR_DEBUG("Mount %s with detected format %s", src, format); + if (mount(src, fs->dst, format, fsflags, NULL) < 0) { + virReportSystemError(errno, + _("Failed to mount device %s to %s as %s"), + src, fs->dst, format); + goto cleanup; + } + ret = 0; + } else { + ret = lxcContainerMountFSBlockAuto(fs, fsflags, src, srcprefix); + } cleanup: return ret; -- 1.7.6.4

On 11/01/2011 09:02 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The LXC code for mounting container filesystems from block devices tries all filesystems in /etc/filesystems and possibly those in /proc/filesystems. The regular mount binary, however, first tries using libblkid to detect the format. Add support for doing the same in libvirt, since Fedora's /etc/filesystems is missing many formats, most notably ext4 which is the default filesystem Fedora uses!
* src/Makefile.am: Link libvirt_lxc to libblkid * src/lxc/lxc_container.c: Probe filesystem format with liblkid
s/liblkid/libblkid/
--- src/Makefile.am | 4 ++ src/lxc/lxc_container.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletions(-)
configure.ac already unconditionally checked for libblkid, so this is simple enough to start using.
+#ifdef HAVE_LIBBLKID +static int +lxcContainerMountDetectFilesystem(const char *src, char **type) +{ + int fd; + int ret = -1; + int rc; + const char *data = NULL; + blkid_probe blkid = NULL; + + *type = NULL; + + if ((fd = open(src, O_RDONLY))< 0) { + virReportSystemError(errno, + _("Unable to open filesystem %s"), src); + return -1;
If we fail to open() the file, can't we at least fall back to the mount probing? That is, what do we gain by returning failure here, instead of returning 0 and letting the fallback code be attempted?
+ } + + if (!(blkid = blkid_new_probe())) { + virReportSystemError(errno, "%s", + _("Unable to create blkid library handle")); + goto cleanup; + } + if (blkid_probe_set_device(blkid, fd, 0, 0)< 0) { + virReportSystemError(EINVAL, + _("Unable to associated device %s with blkid library"), + src);
s/associated/associate/
+ goto cleanup; + } + + blkid_probe_enable_superblocks(blkid, 1); + + blkid_probe_set_superblocks_flags(blkid, BLKID_SUBLKS_TYPE); + + rc = blkid_do_safeprobe(blkid); + if (rc != 0) { + if (rc == 1) /* Nothing found, return success with *type == NULL */ + goto done; + + if (rc == -2) { + virReportSystemError(EINVAL, + _("Too many filesystems detected for %s"), + src); + } else { + virReportSystemError(errno, + _("Unable to detect filesystem for %s"), + src); + } + goto cleanup; + } + + if (blkid_probe_lookup_value(blkid, "TYPE",&data, NULL)< 0) { + virReportSystemError(ENOENT, + _("Unable to find filesystem type for %s"), + src); + goto cleanup; + } + + if (!(*type = strdup(data))) { + virReportOOMError(); + goto cleanup; + } + +done: + ret = 0; +cleanup: + VIR_FORCE_CLOSE(fd); + blkid_free_probe(blkid);
Is this safe? In storage_backed_fs.c, you check that probe != NULL before calling blkid_free_probe. If this function is free-like, we should add it to cfg.mk and update storage_backend_fs.c to get rid of the useless conditional; if not, then this needs fixing to avoid crashing libblkid on a NULL deref.
+ return ret; +} +#else /* ! HAVE_LIBBLKID */ +static int +lxcContainerMountDetectFilesystem(const char *src ATTRIBUTE_UNUSED, + char **type) +{ + /* No libblkid, so just return success with no detected type */ + *type = NULL; + return 0; +} +#endif /* ! HAVE_LIBBLKID */
/* * This functions attempts to do automatic detection of filesystem @@ -771,6 +855,8 @@ static int lxcContainerMountFSBlockHelper(virDomainFSDefPtr fs, { int fsflags = 0; int ret = -1; + char *format = NULL; + if (fs->readonly) fsflags |= MS_RDONLY;
@@ -781,7 +867,21 @@ static int lxcContainerMountFSBlockHelper(virDomainFSDefPtr fs, goto cleanup; }
- ret = lxcContainerMountFSBlockAuto(fs, fsflags, src, srcprefix); + if (lxcContainerMountDetectFilesystem(src,&format)< 0) + goto cleanup;
Given my earlier question, about the only failures that should avoid the fallback would be OOM failure. But since OOM is a real possibility, I agree with the signature (return 0 on success, whether or not format == NULL).
+ + if (format) { + VIR_DEBUG("Mount %s with detected format %s", src, format); + if (mount(src, fs->dst, format, fsflags, NULL)< 0) { + virReportSystemError(errno, + _("Failed to mount device %s to %s as %s"), + src, fs->dst, format); + goto cleanup; + } + ret = 0; + } else { + ret = lxcContainerMountFSBlockAuto(fs, fsflags, src, srcprefix); + }
This part makes sense. Overall, I like the idea, but it may be worth a v2 depending on the answers to my questions above. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Tue, Nov 01, 2011 at 09:21:50AM -0600, Eric Blake wrote:
On 11/01/2011 09:02 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange"<berrange@redhat.com>
The LXC code for mounting container filesystems from block devices tries all filesystems in /etc/filesystems and possibly those in /proc/filesystems. The regular mount binary, however, first tries using libblkid to detect the format. Add support for doing the same in libvirt, since Fedora's /etc/filesystems is missing many formats, most notably ext4 which is the default filesystem Fedora uses!
* src/Makefile.am: Link libvirt_lxc to libblkid * src/lxc/lxc_container.c: Probe filesystem format with liblkid
s/liblkid/libblkid/
--- src/Makefile.am | 4 ++ src/lxc/lxc_container.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletions(-)
configure.ac already unconditionally checked for libblkid, so this is simple enough to start using.
+#ifdef HAVE_LIBBLKID +static int +lxcContainerMountDetectFilesystem(const char *src, char **type) +{ + int fd; + int ret = -1; + int rc; + const char *data = NULL; + blkid_probe blkid = NULL; + + *type = NULL; + + if ((fd = open(src, O_RDONLY))< 0) { + virReportSystemError(errno, + _("Unable to open filesystem %s"), src); + return -1;
If we fail to open() the file, can't we at least fall back to the mount probing? That is, what do we gain by returning failure here, instead of returning 0 and letting the fallback code be attempted?
If you failed to open "src", then you're not going to succeed in mount'ing "src" later, so I don't see a point in letting it continue with the fallback code. It just means the clear error you get here, may well be replaced with a less clear error later.
+ if (!(blkid = blkid_new_probe())) { + virReportSystemError(errno, "%s", + _("Unable to create blkid library handle")); + goto cleanup; + }
+ +done: + ret = 0; +cleanup: + VIR_FORCE_CLOSE(fd); + blkid_free_probe(blkid);
Is this safe? In storage_backed_fs.c, you check that probe != NULL before calling blkid_free_probe. If this function is free-like, we should add it to cfg.mk and update storage_backend_fs.c to get rid of the useless conditional; if not, then this needs fixing to avoid crashing libblkid on a NULL deref.
Opps, it was safe originally, but then I made blkid_new_probe jump to cleanup on failure, so we do need the check Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
Eric Blake