[buildd-tools-devel] [PATCH 05/17] [chroot_mountable] Add filesystem UUID detection

Jan-Marek Glogowski glogow at fbihome.de
Tue Jun 30 18:05:43 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.
---
 configure.ac                         |   14 +++++-
 debian/control                       |    2 +-
 etc/setup.d/10mount                  |   52 +++++++++++++++++++++
 sbuild/Makefile.am                   |    5 +-
 sbuild/sbuild-chroot-block-device.cc |    1 +
 sbuild/sbuild-chroot-mountable.cc    |   48 +++++++++++++++++--
 sbuild/sbuild-chroot-mountable.h     |   50 +++++++++++++++++++-
 sbuild/sbuild-util.cc                |   85 ++++++++++++++++++++++++++++++++++
 sbuild/sbuild-util.h                 |   16 ++++++
 test/Makefile.am                     |    9 +++-
 test/sbuild-util.cc                  |   15 ++++++
 test/setup-test-data                 |    7 ---
 test/setup-test-data.in              |   27 +++++++++++
 13 files changed, 310 insertions(+), 21 deletions(-)
 delete mode 100755 test/setup-test-data
 create mode 100755 test/setup-test-data.in

diff --git a/configure.ac b/configure.ac
index 89bee1d..dac5306 100644
--- a/configure.ac
+++ b/configure.ac
@@ -263,16 +263,25 @@ AM_CONDITIONAL([BUILD_DOXYGEN], [test -n "$DOXYGEN" && test "$enable_doxygen" =
 
 # Checks for libraries.
 PKG_CHECK_MODULES([UUID], [uuid],
-                  [AC_DEFINE(HAVE_UUID)
+                  [AC_DEFINE(HAVE_UUID, 1, [Have libuuid])
 		   HAVE_UUID=yes],
                   [HAVE_UUID=no])
 
-AM_PATH_CPPUNIT([1.10.0], [HAVE_CPPUNIT=yes])
+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=yes],
+                  [HAVE_VOLUME_ID=no])
+
+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])
 
@@ -710,6 +719,7 @@ AC_CONFIG_FILES([man/dchroot-dsa.1])
 AC_CONFIG_FILES([man/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 9835eaf..3543ea7 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.38-dev (>= 1.34.0), libboost-program-options1.38-dev (>= 1.34.0), libboost-regex1.38-dev (>= 1.34.0), libboost-filesystem1.38-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.38-dev (>= 1.38.0), libboost-program-options1.38-dev (>= 1.38.0), libboost-regex1.38-dev (>= 1.38.0), libboost-filesystem1.38-dev (>= 1.38.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/etc/setup.d/10mount b/etc/setup.d/10mount
index 2463a06..8083b87 100755
--- a/etc/setup.d/10mount
+++ b/etc/setup.d/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()
@@ -176,6 +208,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
@@ -201,6 +241,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/sbuild/Makefile.am b/sbuild/Makefile.am
index a68bbad..c8cb481 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
 
@@ -161,7 +161,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-block-device.cc b/sbuild/sbuild-chroot-block-device.cc
index 2899f68..552cdb6 100644
--- a/sbuild/sbuild-chroot-block-device.cc
+++ b/sbuild/sbuild-chroot-block-device.cc
@@ -60,6 +60,7 @@ chroot_block_device::set_device (std::string const& device)
   if (!is_absname(device))
     throw error(device, DEVICE_ABS);
 
+  set_mount_uuid_autodetection();
   this->device = device;
   /** @todo When using LVM snapshots, this it is incorrect to call
    * set_mount_device here, since it is not the mount device.  This
diff --git a/sbuild/sbuild-chroot-mountable.cc b/sbuild/sbuild-chroot-mountable.cc
index a001580..6c27b3e 100644
--- a/sbuild/sbuild-chroot-mountable.cc
+++ b/sbuild/sbuild-chroot-mountable.cc
@@ -35,7 +35,9 @@ chroot_mountable::chroot_mountable ():
   chroot(),
   mount_device(),
   mount_options(),
-  location()
+  location(),
+  mount_uuid(),
+  mount_uuid_autodetection(true)
 {
 }
 
@@ -53,6 +55,7 @@ void
 chroot_mountable::set_mount_device (std::string const& mount_device)
 {
   this->mount_device = mount_device;
+  set_mount_uuid_autodetection();
 }
 
 std::string const&
@@ -82,6 +85,35 @@ chroot_mountable::set_location (std::string const& location)
   this->location = location;
 }
 
+void
+chroot_mountable::set_mount_uuid_autodetection ()
+{
+  mount_uuid_autodetection = true;
+  mount_uuid.clear();
+}
+
+void
+chroot_mountable::set_mount_uuid (std::string const& uuid)
+{
+  mount_uuid_autodetection = false;
+  mount_uuid = uuid;
+}
+
+std::string const&
+chroot_mountable::get_mount_uuid (bool abort_on_drop_privileges)
+{
+  if (has_mount_uuid())
+    return mount_uuid;
+
+  std::string filesystem = get_mount_device();
+  std::string str_uuid;
+  if (!filesystem.empty())
+    get_filesystem_uuid(filesystem, abort_on_drop_privileges, str_uuid);
+  set_mount_uuid(str_uuid);
+
+  return mount_uuid;
+}
+
 std::string
 chroot_mountable::get_path () const
 {
@@ -91,11 +123,12 @@ chroot_mountable::get_path () const
 void
 chroot_mountable::setup_env (environment& env)
 {
-  this->chroot::setup_env(env);
+  chroot::setup_env(env);
 
   env.add("CHROOT_MOUNT_DEVICE", get_mount_device());
   env.add("CHROOT_MOUNT_OPTIONS", get_mount_options());
   env.add("CHROOT_LOCATION", get_location());
+  env.add("CHROOT_MOUNT_UUID", get_mount_uuid(false));
 }
 
 sbuild::chroot::session_flags
@@ -107,9 +140,9 @@ chroot_mountable::get_session_flags () const
 void
 chroot_mountable::get_details (format_detail& detail) const
 {
-  this->chroot::get_details(detail);
+  chroot::get_details(detail);
 
-  if (!this->mount_options.empty())
+  if (!mount_options.empty())
     detail.add(_("Mount Options"), get_mount_options());
   if (!get_location().empty())
     detail.add(_("Location"), get_location());
@@ -143,3 +176,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_autodetection;
+}
+
diff --git a/sbuild/sbuild-chroot-mountable.h b/sbuild/sbuild-chroot-mountable.h
index 274932e..8ac7de2 100644
--- a/sbuild/sbuild-chroot-mountable.h
+++ b/sbuild/sbuild-chroot-mountable.h
@@ -83,14 +83,43 @@ namespace sbuild
     get_location () const;
 
     /**
-     * Set the location.  This is a path to the chroot directory
-     * inside the device (absolute path from the device root).
+     * Set the device relative 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 void
+    void
     set_location (std::string const& location);
 
+    /**
+     * Sets the UUID for the mount_device.
+     *
+     * Additionally this disables the autodetection.
+     *
+     * @param uuid the uuid of the mount_device.
+     */
+    virtual void
+    set_mount_uuid (std::string const& uuid);
+
+    /**
+     * Enables the UUID autodetection.
+     */
+    void
+    set_mount_uuid_autodetection ();
+
+    /**
+     * 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 std::string
     get_path () const;
 
@@ -111,6 +140,17 @@ 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 device to mount.
     std::string mount_device;
@@ -118,6 +158,10 @@ namespace sbuild
     std::string mount_options;
     /// Location inside the mount location root.
     std::string location;
+    /// The UUID cache for the mount device.
+    std::string  mount_uuid;
+    /// UUID autodetection.
+    bool         mount_uuid_autodetection;
   };
 
 }
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 0a07f84..c4864db 100644
--- a/sbuild/sbuild-util.h
+++ b/sbuild/sbuild-util.h
@@ -222,6 +222,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 7c29440..4a4cd31 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -21,7 +21,8 @@
 
 include $(top_srcdir)/scripts/global.mk
 
-LOCAL_CXXFLAGS = $(SCHROOT_CFLAGS) $(CPPUNIT_CFLAGS) -DTESTDATADIR='"./testdata"' -I$(top_srcdir)/bin
+LOCAL_CXXFLAGS = $(SCHROOT_CFLAGS) $(CPPUNIT_CFLAGS) -I$(top_srcdir)/bin \
+	-DTESTDATADIR='"./testdata"' -DFS_UUID='"$(FS_UUID)"'
 
 if USE_UNIT_TESTS
 noinst_LTLIBRARIES = libtest.la
@@ -142,8 +143,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.3.2




More information about the Buildd-tools-devel mailing list