[Buildd-tools-devel] [PATCH 18/22] Add filesystem UUID detection support

Jan-Marek Glogowski glogow at fbihome.de
Thu Mar 26 21:13:56 UTC 2009


This allows to detect or set a filesystems UUID.  It currently
supports detecting the UUID using libvolume_id.  This is used
to mount loopback chroots to a unique directory, so the
filesystem can be used for multiple filesystem union sessions.
---
 bin/schroot/setup/10mount         |   54 +++++++++++++++++++++++-
 configure.ac                      |   12 +++++-
 debian/control                    |    2 +-
 sbuild/Makefile.am                |    5 +-
 sbuild/sbuild-chroot-mountable.cc |   71 ++++++++++++++++++++++++++-----
 sbuild/sbuild-chroot-mountable.h  |   48 ++++++++++++++++----
 sbuild/sbuild-util.cc             |   85 +++++++++++++++++++++++++++++++++++++
 sbuild/sbuild-util.h              |   16 +++++++
 test/Makefile.am                  |    8 +++-
 test/sbuild-util.cc               |   15 +++++++
 test/setup-test-data              |    7 ---
 test/setup-test-data.in           |   27 ++++++++++++
 12 files changed, 315 insertions(+), 35 deletions(-)
 delete mode 100755 test/setup-test-data
 create mode 100755 test/setup-test-data.in

diff --git a/bin/schroot/setup/10mount b/bin/schroot/setup/10mount
index 0a5dc65..a678778 100755
--- a/bin/schroot/setup/10mount
+++ b/bin/schroot/setup/10mount
@@ -52,6 +52,38 @@ do_mount()
     return $RESULT
 }
 
+# Find mount point for block device
+# $1: block device
+get_mount_point()
+{
+    df "${1}" 2>/dev/null | sed -n -e "s#^${1} ##p" | sed -n -e "s#[^/]\+##p"
+}
+
+# Mounts a block-device ro by UUID to ${MOUNT_DIR}/<UUID>
+# $1: options
+# $2: block device
+do_mount_block_device()
+{
+    if [ ! -b "${2}" ] || [ "x" = "x${CHROOT_MOUNT_UUID}" ]; then
+	return 255
+    fi
+
+    UUID_MOUNT="${MOUNT_DIR}/${CHROOT_MOUNT_UUID}"
+
+    MOUNT_POINT=$(get_mount_point "${2}")
+    if [ "x" = "x${MOUNT_POINT}" ]; then
+	do_mount "${1} -o ro" "${2}" "${UUID_MOUNT}"
+        return $?
+    else
+	if [ "x${MOUNT_POINT}" = "x${UUID_MOUNT}" ]; then
+	    return 0
+	else
+	    echo "The device '${2}' is already in use on '${MOUNT_POINT}'"
+	    return 254
+	fi
+    fi
+}
+
 # Unmount all filesystems under specified location
 # $1: mount base location
 do_umount_all()
@@ -117,7 +149,7 @@ do_mount_fs_union()
     fi
 
     if [ "$AUTH_VERBOSITY" = "verbose" ]; then
-	echo "Using '$TYPE' for filesystem union"
+	echo "Using '$CHROOT_FS_UNION_TYPE' for filesystem union"
     fi
 
     return 0
@@ -181,6 +213,14 @@ if [ "$CHROOT_TYPE" = "plain" ] || [ "$CHROOT_TYPE" = "directory" ] || [ "$CHROO
 	if [ "xyes" = "x${CREATE_FS_UNION}" ]; then
 	    if ! do_mount_fs_union "$CHROOT_FS_UNION_RO_BRANCH"
 	    then
+		if [ "x" != "x${UUID_MOUNT}" ]; then
+		    set +e
+		    if umount "${UUID_MOUNT}" 2>/dev/null
+		    then
+			rmdir "${UUID_MOUNT}" 2>/dev/null
+		    fi
+		    set -e
+		fi
 		exit 1
 	    fi
 	else
@@ -206,6 +246,18 @@ if [ "$CHROOT_TYPE" = "plain" ] || [ "$CHROOT_TYPE" = "directory" ] || [ "$CHROO
 
 	do_umount_all "$CHROOT_MOUNT_LOCATION"
 
+	if [ "xyes" = "x${CREATE_FS_UNION}" ] \
+	    && [ "x" != "x${CHROOT_MOUNT_UUID}" ];
+	then
+	    UUID_MOUNT="${MOUNT_DIR}/${CHROOT_MOUNT_UUID}"
+	    set +e
+	    if umount "${UUID_MOUNT}" 2>/dev/null
+	    then
+		rmdir "${UUID_MOUNT}" 2>/dev/null
+	    fi
+	    set -e
+	fi
+
 	if [ "$CHROOT_TYPE" != "file" ]; then
 	    if echo "$CHROOT_MOUNT_LOCATION" | grep -q "^$MOUNT_DIR/"; then
 		if [ -d "$CHROOT_MOUNT_LOCATION" ]; then
diff --git a/configure.ac b/configure.ac
index a85cd91..2612534 100644
--- a/configure.ac
+++ b/configure.ac
@@ -200,16 +200,25 @@ AM_CONDITIONAL([USE_DOXYGEN], [test -n "$DOXYGEN"])
 # Checks for libraries.
 AH_TEMPLATE(HAVE_UUID, [Is libuuid available])
 PKG_CHECK_MODULES([UUID], [uuid],
-                  [AC_DEFINE(HAVE_UUID)
+                  [AC_DEFINE(HAVE_UUID, 1, [Have libuuid])
 		   HAVE_UUID=true],
                   [HAVE_UUID=false])
 
+AH_TEMPLATE(HAVE_VOLUME_ID, [Is libvolume_id available])
+PKG_CHECK_MODULES([VOLUME_ID], [libvolume_id],
+                  [AC_DEFINE(HAVE_VOLUME_ID, 1, [Have libvolume_id])
+		   HAVE_VOLUME_ID=true],
+                  [HAVE_VOLUME_ID=false])
+
 AM_PATH_CPPUNIT([1.10.0], [HAVE_CPPUNIT=true])
 AM_CONDITIONAL([USE_UNIT_TESTS], [test -n "$HAVE_CPPUNIT"])
 
 SCHROOT_CFLAGS="$UUID_CFLAGS"
 AC_SUBST([SCHROOT_CFLAGS])
 
+FS_UUID="59a6643d-e2a7-4d54-bfa6-eec927ffa181"
+AC_SUBST([FS_UUID])
+
 # Checks for header files.
 AC_CHECK_HEADERS([tr1/memory])
 
@@ -502,6 +511,7 @@ AC_CONFIG_FILES([bin/csbuild/Makefile])
 AC_CONFIG_FILES([bin/csbuild/csbuild.1])
 AC_CONFIG_FILES([scripts/po-notify])
 AC_CONFIG_FILES([test/Makefile])
+AC_CONFIG_FILES([test/setup-test-data])
 AC_CONFIG_FILES([Makefile])
 dnl Output the generated config.status script.
 AC_OUTPUT
diff --git a/debian/control b/debian/control
index 8df5779..812311f 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,7 @@ Section: admin
 Priority: optional
 Maintainer: Debian buildd-tools Developers <buildd-tools-devel at lists.alioth.debian.org>
 Uploaders: Michael Banck <mbanck at debian.org>, Luk Claes <luk at debian.org>, Roger Leigh <rleigh at debian.org>, Francesco Paolo Lovergine <frankie at debian.org>
-Build-Depends: debhelper (>= 7.0.0), autotools-dev, pkg-config (>= 0.20), libpam0g-dev (>= 0.79-3.1), uuid-dev, liblockdev1-dev (>= 1.0.2), libboost1.37-dev (>= 1.34.0), libboost-program-options1.37-dev (>= 1.34.0), libboost-regex1.37-dev (>= 1.34.0), libboost-filesystem1.37-dev (>= 1.34.0), gettext, libcppunit-dev, doxygen, graphviz
+Build-Depends: debhelper (>= 7.0.0), autotools-dev, pkg-config (>= 0.20), libpam0g-dev (>= 0.79-3.1), uuid-dev, liblockdev1-dev (>= 1.0.2), libboost1.37-dev (>= 1.34.0), libboost-program-options1.37-dev (>= 1.34.0), libboost-regex1.37-dev (>= 1.34.0), libboost-filesystem1.37-dev (>= 1.34.0), gettext, libcppunit-dev, doxygen, graphviz, libvolume-id-dev, e2fsprogs
 Standards-Version: 3.8.1
 Vcs-Browser: http://git.debian.org/?p=buildd-tools/schroot.git
 Vcs-Git: git://git.debian.org/git/buildd-tools/schroot
diff --git a/sbuild/Makefile.am b/sbuild/Makefile.am
index 400fcc0..2427004 100644
--- a/sbuild/Makefile.am
+++ b/sbuild/Makefile.am
@@ -21,7 +21,7 @@
 
 include $(top_srcdir)/scripts/global.mk
 
-LOCAL_CXXFLAGS = $(SCHROOT_CFLAGS)
+LOCAL_CXXFLAGS = $(SCHROOT_CFLAGS) $(VOLUME_ID_CFLAGS)
 
 DEFS = -D_GNU_SOURCE
 
@@ -115,7 +115,8 @@ libsbuild_la_SOURCES =			\
 nodist_libsbuild_la_SOURCES =	\
 	sbuild-config.h
 
-libsbuild_la_LIBADD = $(UUID_LIBS) $(PAM_LIBS) $(LOCKDEV_LIBS) $(BOOST_LIBS) $(LIBINTL)
+libsbuild_la_LIBADD = $(UUID_LIBS) $(PAM_LIBS) $(LOCKDEV_LIBS) $(BOOST_LIBS) \
+	$(LIBINTL) $(VOLUME_ID_LIBS)
 
 pkgconfigdatadir = $(libdir)/pkgconfig
 
diff --git a/sbuild/sbuild-chroot-mountable.cc b/sbuild/sbuild-chroot-mountable.cc
index 548a709..06e63a6 100644
--- a/sbuild/sbuild-chroot-mountable.cc
+++ b/sbuild/sbuild-chroot-mountable.cc
@@ -33,12 +33,15 @@ using namespace sbuild;
 
 chroot_mountable::chroot_mountable ():
   chroot(),
-  mount_options()
+  mount_options(),
+  mount_uuid(NULL)
 {
 }
 
 chroot_mountable::~chroot_mountable ()
 {
+  if (mount_uuid != NULL)
+    delete mount_uuid;
 }
 
 std::string const&
@@ -53,27 +56,65 @@ chroot_mountable::set_mount_options (std::string const& mount_options)
   this->mount_options = mount_options;
 }
 
-std::string const&
-chroot_mountable::get_location () const
+void
+chroot_mountable::setup_env (environment& env)
+{
+  chroot::setup_env(env);
+
+  env.add("CHROOT_MOUNT_OPTIONS", get_mount_options());
+  env.add("CHROOT_MOUNT_UUID", get_mount_uuid(false));
+}
+
+void
+chroot_mountable::set_container (std::string const& container)
 {
-  return chroot::get_location();
+  set_mount_uuid(NULL);
+  chroot::set_container(container);
 }
 
 void
 chroot_mountable::set_location (std::string const& location)
 {
-  if (!location.empty() && !is_absname(location))
-    throw error(location, LOCATION_ABS);
-
   chroot::set_location(location);
 }
 
 void
-chroot_mountable::setup_env (environment& env)
+chroot_mountable::set_mount_uuid (std::string const* uuid)
 {
-  this->chroot::setup_env(env);
+  if (uuid == NULL)
+    {
+      if (mount_uuid != NULL)
+        {
+          delete mount_uuid;
+          mount_uuid = NULL;
+        }
+    }
+  else
+    {
+      if (mount_uuid != NULL)
+        *mount_uuid = *uuid;
+      else
+        mount_uuid = new std::string(*uuid);
+    }
+}
 
-  env.add("CHROOT_MOUNT_OPTIONS", get_mount_options());
+std::string const&
+chroot_mountable::get_mount_uuid (bool abort_on_drop_privileges)
+{
+  if (has_mount_uuid())
+    return *mount_uuid;
+
+  std::string filesystem = get_container();
+  std::string str_uuid;
+  if (filesystem.empty() ||
+      !get_filesystem_uuid(filesystem, abort_on_drop_privileges, str_uuid))
+    {
+      mount_uuid = new std::string();
+    }
+  else
+    set_mount_uuid(&str_uuid);
+
+  return *mount_uuid;
 }
 
 sbuild::chroot::session_flags
@@ -99,7 +140,8 @@ chroot_mountable::get_keyfile (keyfile& keyfile) const
   keyfile::set_object_value(*this, &chroot_mountable::get_mount_options,
 			    keyfile, get_name(), "mount-options");
 
-  keyfile::set_object_value(*this, &chroot_mountable::get_location,
+  keyfile::set_object_value(*(static_cast<const chroot *>(this)),
+			    &chroot::get_location,
 			    keyfile, get_name(), "location");
 }
 
@@ -119,3 +161,10 @@ chroot_mountable::set_keyfile (keyfile const& keyfile,
 			    keyfile::PRIORITY_OPTIONAL);
   used_keys.push_back("location");
 }
+
+bool
+chroot_mountable::has_mount_uuid () const
+{
+  return (mount_uuid != NULL);
+}
+
diff --git a/sbuild/sbuild-chroot-mountable.h b/sbuild/sbuild-chroot-mountable.h
index 206d6b0..8d7b701 100644
--- a/sbuild/sbuild-chroot-mountable.h
+++ b/sbuild/sbuild-chroot-mountable.h
@@ -58,22 +58,37 @@ namespace sbuild
     set_mount_options (std::string const& mount_options);
 
     /**
-     * Get the location.  This is a path to the chroot directory
-     * inside the device (absolute path from the device root).
+     * Set the device relative location.
      *
-     * @returns the location.
+     * This is a path to the chroot directory inside the device 
+     * (absolute path from the device root).  Even through it has to 
+     * be an absolute path.
+     *
+     * @param location the location.
      */
-    virtual std::string const&
-    get_location () const;
+    void
+    set_location (std::string const& location);
 
     /**
-     * Set the location.  This is a path to the chroot directory
-     * inside the device (absolute path from the device root).
+     * Sets the UUID for the mount_device.
      *
-     * @param location the location.
+     * @param uuid the uuid of the mount_device.
      */
     virtual void
-    set_location (std::string const& location);
+    set_mount_uuid (std::string const* uuid);
+
+    virtual void
+    set_container (std::string const& container);
+
+    /**
+     * Gets the UUID of the filesystem on the mount_device.
+     *
+     * @param abort_on_error if true throws an exception, if no uuid could
+     * be found or generated.
+     * @return the uuid or an empty string.
+     */
+    virtual std::string const&
+    get_mount_uuid (bool abort_on_error = false);
 
     virtual void
     setup_env (environment& env);
@@ -92,9 +107,22 @@ namespace sbuild
     set_keyfile (keyfile const& keyfile,
 		 string_list&   used_keys);
 
+    /**
+     * Returns the status of the UUID detection.
+     *
+     * This basically just prevents direct access to mount_uuid and allows
+     * reimplementation of get_mount_uuid detect the mount_uuid status.
+     *
+     * @return true, if mount_uuid is set and UUID detection was run.
+     */
+    bool
+    has_mount_uuid () const;
+
   private:
     /// The options to mount the device with.
-    std::string mount_options;
+    std::string  mount_options;
+    /// The UUID cache for the mount device.
+    std::string *mount_uuid;
   };
 
 }
diff --git a/sbuild/sbuild-util.cc b/sbuild/sbuild-util.cc
index 1cfa680..132578f 100644
--- a/sbuild/sbuild-util.cc
+++ b/sbuild/sbuild-util.cc
@@ -29,6 +29,21 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#ifdef HAVE_VOLUME_ID
+extern "C" {
+#undef __cplusplus
+#include <libvolume_id.h>
+#define __cplusplus
+}
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#endif
+
 using namespace sbuild;
 
 namespace
@@ -406,6 +421,76 @@ sbuild::exec (std::string const& file,
   return status;
 }
 
+bool
+sbuild::get_filesystem_uuid(std::string const& filesystem,
+			    bool abort_on_drop_privileges,
+			    std::string& uuid)
+{
+  bool detected_uuid = false;
+
+#ifdef HAVE_VOLUME_ID
+  // This code is inspired by udevs vol_id program
+  struct volume_id *vol_id;
+  int retval;
+  int fd;
+
+  if (filesystem.empty())
+    goto return_filesystem_uuid;
+
+  fd = open(filesystem.c_str(), O_RDONLY | O_EXCL);
+  if (fd == -1)
+    goto return_filesystem_uuid;
+
+  vol_id = volume_id_open_fd(fd);
+  if (vol_id == NULL)
+    goto bailout_filesystem_uuid;
+
+  uint64_t size;
+  if (ioctl(fd, BLKGETSIZE64, &size) != 0)
+    size = 0;
+
+  if (getuid() == 0)
+    {
+      struct passwd *pw;
+
+      pw = getpwnam("nobody");
+      if (pw != NULL && pw->pw_uid > 0 && pw->pw_gid > 0)
+	{
+	  if (abort_on_drop_privileges &&
+		(setgroups(0, NULL) != 0 ||
+		 setgid(pw->pw_gid) != 0 ||
+		 setuid(pw->pw_uid) != 0))
+	    goto bailout_filesystem_uuid;
+	}
+    }
+
+  retval = volume_id_probe_filesystem(vol_id, 0, size);
+  if (retval != 0)
+    goto bailout_filesystem_uuid;
+
+  const char *c_uuid;
+  retval = volume_id_get_uuid(vol_id, &c_uuid);
+  if (retval == 0)
+    goto bailout_filesystem_uuid;
+
+  detected_uuid = true;
+
+  char uuid_enc[256];
+  volume_id_encode_string(c_uuid, uuid_enc, sizeof(uuid_enc));
+
+  uuid = std::string(uuid_enc);
+
+bailout_filesystem_uuid:
+  if (vol_id != NULL)
+    volume_id_close(vol_id);
+
+  close(fd);
+#endif
+
+return_filesystem_uuid:
+  return detected_uuid;
+}
+
 sbuild::stat::stat (std::string const& file):
   file(file),
   fd(0),
diff --git a/sbuild/sbuild-util.h b/sbuild/sbuild-util.h
index 24c16cb..b8212ad 100644
--- a/sbuild/sbuild-util.h
+++ b/sbuild/sbuild-util.h
@@ -196,6 +196,22 @@ namespace sbuild
 	environment const& env);
 
   /**
+   * Get the UUID of a filesystem.
+   *
+   * This function gets the filesystem UUID from a block device or a
+   * filesystem contained in a file.
+   *
+   * @param filesystem the path to the filesystem device or file.
+   * @param abort_on_drop_privileges abort if privileges drop failed.
+   * @param uuid the uuid string.
+   * @returns true on success, false on error.
+   */
+  bool
+  get_filesystem_uuid(std::string const& filesystem,
+		      bool abort_on_drop_privileges,
+		      std::string& uuid);
+
+  /**
    * Get file status.  stat(2) wrapper.
    */
   class stat
diff --git a/test/Makefile.am b/test/Makefile.am
index ae7dcc1..60414b1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -22,7 +22,7 @@
 include $(top_srcdir)/scripts/global.mk
 
 LOCAL_CXXFLAGS = $(SCHROOT_CFLAGS) $(CPPUNIT_CFLAGS) -I$(top_srcdir)/bin \
-	-DTESTDATADIR='"./testdata"'
+	-DTESTDATADIR='"./testdata"' -DFS_UUID='"$(FS_UUID)"'
 
 if USE_UNIT_TESTS
 noinst_LTLIBRARIES = libtest.la
@@ -133,8 +133,12 @@ EXTRA_DIST = 				\
 	run-parts.ex1/30test3		\
 	run-parts.ex2			\
 	run-parts.ex3/50invalid		\
-	setup-test-data			\
+	setup-test-data.in		\
 	cleanup-test-data
 
 clean-local:
 	$(srcdir)/cleanup-test-data
+
+all-local:
+	chmod a+rx ./setup-test-data
+
diff --git a/test/sbuild-util.cc b/test/sbuild-util.cc
index 603cf54..94c1488 100644
--- a/test/sbuild-util.cc
+++ b/test/sbuild-util.cc
@@ -22,6 +22,8 @@
 
 #include <cppunit/extensions/HelperMacros.h>
 
+#include "config.h"
+
 using namespace CppUnit;
 
 class test_util : public TestCase
@@ -32,6 +34,9 @@ class test_util : public TestCase
   CPPUNIT_TEST(test_string_list_to_string);
   CPPUNIT_TEST(test_split_string);
   CPPUNIT_TEST(test_find_program_in_path);
+#ifdef HAVE_VOLUME_ID
+  CPPUNIT_TEST(test_read_uuid);
+#endif
   CPPUNIT_TEST_SUITE_END();
 
 public:
@@ -89,6 +94,16 @@ public:
     CPPUNIT_ASSERT(sbuild::find_program_in_path("sed", path, "") == "/bin/sed");
   }
 
+  void test_read_uuid()
+  {
+    std::string uuid;
+    CPPUNIT_ASSERT(sbuild::get_filesystem_uuid
+		   (TESTDATADIR "/does-not-exist", false, uuid) == false);
+    CPPUNIT_ASSERT(sbuild::get_filesystem_uuid
+		   (TESTDATADIR "/loopback-file", false, uuid) == true);
+    CPPUNIT_ASSERT(uuid == FS_UUID);
+  }
+
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(test_util);
diff --git a/test/setup-test-data b/test/setup-test-data
deleted file mode 100755
index 1785149..0000000
--- a/test/setup-test-data
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-# This script ensures that the test data is owned by root.
-
-rm -rf testdata
-mkdir testdata
-cp -r ${srcdir}/*.ex* testdata
-chown -R root:root testdata
diff --git a/test/setup-test-data.in b/test/setup-test-data.in
new file mode 100755
index 0000000..01fd86f
--- /dev/null
+++ b/test/setup-test-data.in
@@ -0,0 +1,27 @@
+#!/bin/sh
+# This script ensures that the test data is owned by root
+# and creates some additional data for tests.
+
+if [ -d testdata ]; then
+	rm -rf testdata
+fi
+
+if [ $(id -u) -ne 0 ]; then
+	exit 1
+fi
+
+echo "Generating test data..."
+
+mkdir testdata
+
+LOOPBACK=testdata/loopback-file
+UUID=@FS_UUID@
+
+dd if=/dev/zero of=$LOOPBACK bs=1M count=5
+/sbin/mke2fs -F $LOOPBACK 1>/dev/null
+/sbin/tune2fs -U $UUID $LOOPBACK
+
+# chown data
+cp -r ${srcdir}/*.ex* testdata
+chown -R root:root testdata
+
-- 
1.6.2.1




More information about the Buildd-tools-devel mailing list