[Buildd-tools-devel] [PATCH 19/22] Add filesystem union support to loopback chroots

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


As unioning filesystems just work with directories, the 10mount
script first mounts the loopback device to a directory named
after the contained filesystem UUID in the SCHROOT_MOUNT_DIR.
This directory is used as the read-only branch.

As this chroot now inherits from two chroot based classes, it has
to change the behaviour of the chroot::get_keyfile function, which
won't clean the group of the supplied keyfile anymore, otherwise
the keys from the first class (fs-union) will be lost.
---
 bin/schroot/schroot.conf.5.in    |   12 ++-
 bin/schroot/setup/10mount        |   46 ++++++++++-
 sbuild/sbuild-chroot-loopback.cc |   49 ++++++++---
 sbuild/sbuild-chroot-loopback.h  |    7 +-
 sbuild/sbuild-chroot.cc          |    2 -
 test/Makefile.am                 |    1 +
 test/sbuild-chroot-loopback.cc   |  173 ++++++++++++++++++++++++++++++++++++++
 test/test-sbuild-chroot.h        |   15 +++-
 8 files changed, 279 insertions(+), 26 deletions(-)
 create mode 100644 test/sbuild-chroot-loopback.cc

diff --git a/bin/schroot/schroot.conf.5.in b/bin/schroot/schroot.conf.5.in
index b197bce..674470b 100644
--- a/bin/schroot/schroot.conf.5.in
+++ b/bin/schroot/schroot.conf.5.in
@@ -184,12 +184,14 @@ Loopback chroots
 .PP
 Chroots of type \[oq]loopback\[cq] are a filesystem available as a file on
 disk, accessed via a loopback mount.  The file will be loopback mounted and
-unmounted on demand.  They implement the \fBmountable chroot\fP options (see
-\[lq]\fIMountable chroot options\fP\[rq], below), plus an additional option:
+unmounted on demand.  They implement the \fBmountable chroot\fP and 
+\fBfs-union chroot\fP options (see \[lq]\fIMountable chroot options\fP\[rq]
+and \[lq]\fIFilesystem Union chroot options\fP\[rq], below), plus an 
+additional option:
 .TP
-\f[CBI]file=\fP\f[CI]file\fP This is the filename of the file containing the
-filesystem, including the absolute path.  For example,
-\[lq]/srv/chroot/sid\[rq].
+\f[CBI]file=\fP\f[CI]filename\fP
+This is the filename of the file containing the filesystem, including the
+absolute path.  For example \[lq]/srv/chroot/sid\[rq].
 .SS
 Block device chroots
 .PP
diff --git a/bin/schroot/setup/10mount b/bin/schroot/setup/10mount
index a678778..d952db8 100755
--- a/bin/schroot/setup/10mount
+++ b/bin/schroot/setup/10mount
@@ -182,9 +182,6 @@ if [ "$CHROOT_TYPE" = "plain" ] || [ "$CHROOT_TYPE" = "directory" ] || [ "$CHROO
 	UNPACK_LOCATION="${UNPACK_DIR}/${SESSION_ID}"
 	CHROOT_MOUNT_OPTIONS="--bind"
 	CHROOT_MOUNT_DEVICE="$UNPACK_LOCATION"
-    elif [ "$CHROOT_TYPE" = "loopback" ]; then
-	CHROOT_MOUNT_OPTIONS="$CHROOT_MOUNT_OPTIONS -o loop"
-	CHROOT_MOUNT_DEVICE="$CHROOT_FILE"
     elif [ "$CHROOT_TYPE" = "lvm-snapshot" ]; then
 	CHROOT_MOUNT_DEVICE="$CHROOT_LVM_SNAPSHOT_DEVICE"
     elif [ "$CHROOT_TYPE" = "block-device" ]; then
@@ -204,6 +201,39 @@ if [ "$CHROOT_TYPE" = "plain" ] || [ "$CHROOT_TYPE" = "directory" ] || [ "$CHROO
 	    exit 1
 	fi
 
+	if [ "$CHROOT_TYPE" = "loopback" ]; then
+	    LOOP_DEVICE=$(losetup -j ${CHROOT_FILE} | sed -e 's/:.*$//')
+	    if [ "xyes" = "x${CREATE_FS_UNION}" ]; then
+		if [ "x" = "x${LOOP_DEVICE}" ]; then
+		    LOOP_DEVICE=$(losetup -f)
+		    losetup -r "${LOOP_DEVICE}" "${CHROOT_FILE}"
+
+		    if ! do_mount_block_device "${CHROOT_MOUNT_OPTIONS}" "${LOOP_DEVICE}"
+		    then
+			losetup -d ${LOOP_DEVICE}
+			exit 1
+		    fi
+		else
+		    if ! do_mount_block_device "${CHROOT_MOUNT_OPTIONS}" "${LOOP_DEVICE}"
+		    then
+			exit 1
+		    fi
+		fi
+		CHROOT_MOUNT_DEVICE="${LOOP_DEVICE}"
+		CHROOT_MOUNT_OPTIONS=""
+		CHROOT_FS_UNION_RO_BRANCH="${MOUNT_DIR}/${CHROOT_MOUNT_UUID}"
+	    else
+		if [ "x" != "x${LOOP_DEVICE}" ]; then
+		    echo "The file '${CHROOT_FILE}' is already associated" \
+			"with device '${LOOP_DEVICE}'."
+		    exit 1
+		else
+		    CHROOT_MOUNT_DEVICE="${CHROOT_FILE}"
+		    CHROOT_MOUNT_OPTIONS="${CHROOT_MOUNT_OPTIONS} -o loop"
+		fi
+	    fi
+	fi
+
         # If recovering, we want to remount all filesystems to ensure
         # a sane state.
 	if [ $1 = "setup-recover" ]; then
@@ -256,6 +286,16 @@ if [ "$CHROOT_TYPE" = "plain" ] || [ "$CHROOT_TYPE" = "directory" ] || [ "$CHROO
 		rmdir "${UUID_MOUNT}" 2>/dev/null
 	    fi
 	    set -e
+	
+	    if [ "$CHROOT_TYPE" = "loopback" ]; then
+		LOOP_DEVICE=$(losetup -j ${CHROOT_FILE} | sed -e 's/:.*$//')
+		if [ "x" != "x$LOOP_DEVICE" ]; then
+		    MOUNT_POINT=$(get_mount_point "${LOOP_DEVICE}")
+		    if [ "x" = "x$MOUNT_POINT" ]; then
+			losetup -d $LOOP_DEVICE
+		    fi
+		fi
+	    fi
 	fi
 
 	if [ "$CHROOT_TYPE" != "file" ]; then
diff --git a/sbuild/sbuild-chroot-loopback.cc b/sbuild/sbuild-chroot-loopback.cc
index 35fc044..9d748da 100644
--- a/sbuild/sbuild-chroot-loopback.cc
+++ b/sbuild/sbuild-chroot-loopback.cc
@@ -32,7 +32,7 @@ using boost::format;
 using namespace sbuild;
 
 chroot_loopback::chroot_loopback ():
-  chroot(),
+  chroot_fs_union(),
   chroot_mountable()
 {
 }
@@ -47,11 +47,24 @@ chroot_loopback::clone () const
   return ptr(new chroot_loopback(*this));
 }
 
+sbuild::chroot::ptr
+chroot_loopback::clone_source () const
+{
+  ptr clone;
+
+  if (get_fs_union_configured()) {
+    clone = ptr(new chroot_loopback(*this));
+    chroot_source::clone_source_setup(clone);
+  }
+
+  return ptr(clone);
+}
+
 void
 chroot_loopback::set_container (std::string const& file)
 {
   if (!is_absname(file))
-    throw error(file, FILE_ABS);
+    throw chroot::error(file, FILE_ABS);
 
   chroot_mountable::set_container(file);
 }
@@ -59,7 +72,7 @@ chroot_loopback::set_container (std::string const& file)
 void
 chroot_loopback::setup_env (environment& env)
 {
-  chroot::setup_env(env);
+  chroot_fs_union::setup_env(env);
   chroot_mountable::setup_env(env);
 }
 
@@ -76,33 +89,45 @@ chroot_loopback::setup_lock (chroot::setup_type type,
 
       // NOTE: taken from chroot_config::check_security.
       if (file_status.uid() != 0)
-	throw error(file, FILE_OWNER);
+	throw chroot::error(file, FILE_OWNER);
       if (file_status.check_mode(stat::PERM_OTHER_WRITE))
-	throw error(file, FILE_PERMS);
+	throw chroot::error(file, FILE_PERMS);
       if (!file_status.is_regular())
-	throw error(file, FILE_NOTREG);
+	throw chroot::error(file, FILE_NOTREG);
     }
 
-  /* By default, loopback chroots do no locking. */
+  /**
+   * By default, loopback chroots do no locking, but can create sessions
+   * using filesystem unions.
+   */
+  if (get_fs_union_configured() &&
+      ((type == SETUP_START && lock == true) ||
+       (type == SETUP_STOP && lock == false && status == 0)))
+    {
+      bool start = (type == SETUP_START);
+      setup_session_info(start);
+    }
 }
 
 sbuild::chroot::session_flags
 chroot_loopback::get_session_flags () const
 {
-  return SESSION_NOFLAGS | chroot_mountable::get_session_flags();
+  return SESSION_NOFLAGS 
+    | chroot_mountable::get_session_flags()
+    | chroot_fs_union::get_session_flags();
 }
 
 void
 chroot_loopback::get_details (format_detail& detail) const
 {
-  this->chroot::get_details(detail);
-  this->chroot_mountable::get_details(detail);
+  chroot_fs_union::get_details(detail);
+  chroot_mountable::get_details(detail);
 }
 
 void
 chroot_loopback::get_keyfile (keyfile& keyfile) const
 {
-  chroot::get_keyfile(keyfile);
+  chroot_fs_union::get_keyfile(keyfile);
   chroot_mountable::get_keyfile(keyfile);
 }
 
@@ -110,7 +135,7 @@ void
 chroot_loopback::set_keyfile (keyfile const& keyfile,
 			      string_list&   used_keys)
 {
-  chroot::set_keyfile(keyfile, used_keys);
+  chroot_fs_union::set_keyfile(keyfile, used_keys);
   chroot_mountable::set_keyfile(keyfile, used_keys);
 }
 
diff --git a/sbuild/sbuild-chroot-loopback.h b/sbuild/sbuild-chroot-loopback.h
index 91ed269..b27e0ff 100644
--- a/sbuild/sbuild-chroot-loopback.h
+++ b/sbuild/sbuild-chroot-loopback.h
@@ -19,7 +19,7 @@
 #ifndef SBUILD_CHROOT_LOOPBACK_H
 #define SBUILD_CHROOT_LOOPBACK_H
 
-#include <sbuild/sbuild-chroot.h>
+#include <sbuild/sbuild-chroot-fs-union.h>
 #include <sbuild/sbuild-chroot-mountable.h>
 
 namespace sbuild
@@ -30,7 +30,7 @@ namespace sbuild
    *
    * The file will be mounted on demand.
    */
-  class chroot_loopback : virtual public chroot,
+  class chroot_loopback : public chroot_fs_union,
 			  public chroot_mountable
   {
   protected:
@@ -45,6 +45,9 @@ namespace sbuild
 
     virtual chroot::ptr
     clone () const;
+ 
+    virtual chroot::ptr
+    clone_source () const;
 
     /**
      * Set the file containing the chroot.
diff --git a/sbuild/sbuild-chroot.cc b/sbuild/sbuild-chroot.cc
index 41a314c..83db142 100644
--- a/sbuild/sbuild-chroot.cc
+++ b/sbuild/sbuild-chroot.cc
@@ -551,8 +551,6 @@ sbuild::chroot::print_details (std::ostream& stream) const
 void
 sbuild::chroot::get_keyfile (keyfile& keyfile) const
 {
-  keyfile.remove_group(get_name());
-
   std::string type, container_key;
   get_chroot_strings(&type, &container_key, NULL);
 
diff --git a/test/Makefile.am b/test/Makefile.am
index 60414b1..729bf2d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -74,6 +74,7 @@ sbuild_chroot_SOURCES = 		\
 	sbuild-chroot-file.cc		\
 	sbuild-chroot-block-device.cc	\
 	sbuild-chroot-lvm-snapshot.cc	\
+	sbuild-chroot-loopback.cc	\
 	test-sbuild-chroot.h
 sbuild_chroot_LDADD =  libtest.la
 
diff --git a/test/sbuild-chroot-loopback.cc b/test/sbuild-chroot-loopback.cc
new file mode 100644
index 0000000..0852ea8
--- /dev/null
+++ b/test/sbuild-chroot-loopback.cc
@@ -0,0 +1,173 @@
+/* Copyright © 2008-2009  Jan-Marek Glogowski <glogow at fbihome.de>
+ *
+ * schroot 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * schroot 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *********************************************************************/
+
+#include "config.h"
+
+#include <algorithm>
+#include <set>
+
+#include <sbuild/sbuild-chroot-loopback.h>
+
+#include "test-helpers.h"
+#include "test-sbuild-chroot.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <iostream>
+
+using std::cout;
+using std::endl;
+
+using namespace CppUnit;
+
+class chroot_loopback : public sbuild::chroot_loopback
+{
+public:
+  chroot_loopback():
+    sbuild::chroot_loopback()
+  {}
+
+  virtual ~chroot_loopback()
+  {}
+};
+
+class test_chroot_loopback : public test_chroot_base<chroot_loopback>
+{
+  CPPUNIT_TEST_SUITE(test_chroot_loopback);
+  CPPUNIT_TEST(test_file);
+  CPPUNIT_TEST(test_mount_options);
+  CPPUNIT_TEST(test_chroot_strings);
+  CPPUNIT_TEST(test_setup_env);
+  CPPUNIT_TEST(test_setup_env2);
+  CPPUNIT_TEST(test_session_flags);
+  CPPUNIT_TEST(test_print_details);
+  CPPUNIT_TEST(test_print_config);
+  CPPUNIT_TEST_SUITE_END();
+
+protected:
+  std::string loopback_file;
+
+public:
+  test_chroot_loopback():
+    test_chroot_base<chroot_loopback>(),
+    loopback_file()
+  {
+    loopback_file = abs_testdata_dir;
+    loopback_file.append("/loopback-file");
+  }
+
+  void setUp()
+  {
+    test_chroot_base<chroot_loopback>::setUp();
+    sbuild::chroot_loopback *c = dynamic_cast<sbuild::chroot_loopback *>(chroot.get());
+    c->set_mount_options("-t jfs -o quota,rw");
+  }
+
+  void
+  test_file()
+  {
+    sbuild::chroot_loopback *c = dynamic_cast<sbuild::chroot_loopback *>(chroot.get());
+    CPPUNIT_ASSERT(c);
+    c->set_container("/dev/some/file");
+    CPPUNIT_ASSERT(c->get_container() == "/dev/some/file");
+  }
+
+  void
+  test_mount_options()
+  {
+    sbuild::chroot_loopback *c = dynamic_cast<sbuild::chroot_loopback *>(chroot.get());
+    CPPUNIT_ASSERT(c);
+    c->set_mount_options("-o opt1,opt2");
+    CPPUNIT_ASSERT(c->get_mount_options() == "-o opt1,opt2");
+  }
+
+  void test_chroot_strings()
+  {
+    std::string type, key_name, detail_name;
+    chroot->get_chroot_strings(&type, &key_name, &detail_name);
+    
+    CPPUNIT_ASSERT(type == "loopback");
+    CPPUNIT_ASSERT(key_name == "file");
+    CPPUNIT_ASSERT(detail_name == "File");
+  }
+
+  void setup_env_common(sbuild::environment& expected)
+  {
+    expected.add("CHROOT_TYPE",           "loopback");
+    expected.add("CHROOT_NAME",           "test-name");
+    expected.add("CHROOT_DESCRIPTION",    "test-description");
+    expected.add("CHROOT_MOUNT_LOCATION", "/mnt/mount-location");
+    expected.add("CHROOT_PATH",           "/mnt/mount-location");
+    expected.add("CHROOT_MOUNT_OPTIONS",  "-t jfs -o quota,rw");
+    expected.add("CHROOT_SCRIPT_CONFIG",  sbuild::normalname(std::string(PACKAGE_SYSCONF_DIR) + "/script-defaults"));
+    expected.add("CHROOT_SESSION_CLONE",  "false");
+    expected.add("CHROOT_SESSION_CREATE", "false");
+    expected.add("CHROOT_SESSION_PURGE",  "false");
+    expected.add("CHROOT_FS_UNION_TYPE",  "none");
+  }
+
+  void test_setup_env()
+  {
+    sbuild::environment expected;
+    setup_env_common(expected);
+
+    test_chroot_base<chroot_loopback>::test_setup_env(expected);
+  }
+
+  void test_setup_env2()
+  {
+    sbuild::chroot_loopback *c = dynamic_cast<sbuild::chroot_loopback *>(chroot.get());
+    CPPUNIT_ASSERT(c);
+    c->set_container(loopback_file);
+
+    sbuild::environment expected;
+    setup_env_common(expected);
+
+    expected.add("CHROOT_FILE",           loopback_file);
+    expected.add("CHROOT_CONTAINER",      loopback_file);
+    expected.add("CHROOT_MOUNT_UUID",     FS_UUID);
+
+    test_chroot_base<chroot_loopback>::test_setup_env(expected);
+  }
+
+  void test_session_flags()
+  {
+    CPPUNIT_ASSERT(chroot->get_session_flags() ==
+		   sbuild::chroot::SESSION_NOFLAGS);
+  }
+
+  void test_print_details()
+  {
+    std::ostringstream os;
+    os << chroot;
+    // TODO: Compare output.
+    CPPUNIT_ASSERT(!os.str().empty());
+  }
+
+  void test_print_config()
+  {
+    std::ostringstream os;
+    sbuild::keyfile config;
+    config << chroot;
+    os << config;
+    // TODO: Compare output.
+    CPPUNIT_ASSERT(!os.str().empty());
+  }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_chroot_loopback);
diff --git a/test/test-sbuild-chroot.h b/test/test-sbuild-chroot.h
index e7d0c88..60fc7d0 100644
--- a/test/test-sbuild-chroot.h
+++ b/test/test-sbuild-chroot.h
@@ -19,6 +19,8 @@
 #ifndef TEST_SBUILD_CHROOT_H
 #define TEST_SBUILD_CHROOT_H
 
+#include <unistd.h>
+
 #include <sbuild/sbuild-chroot.h>
 
 #include <algorithm>
@@ -34,12 +36,21 @@ class test_chroot_base : public TestFixture
 {
 protected:
   sbuild::chroot::ptr chroot;
+  std::string abs_testdata_dir;
 
 public:
   test_chroot_base():
     TestFixture(),
-    chroot()
-  {}
+    chroot(),
+    abs_testdata_dir()
+  {
+    char cwd[FILENAME_MAX];
+    if (NULL != getcwd(cwd, FILENAME_MAX))
+      {
+        abs_testdata_dir = std::string(cwd);
+        abs_testdata_dir.append("/" TESTDATADIR);
+      }
+  }
 
   virtual ~test_chroot_base()
   {}
-- 
1.6.2.1




More information about the Buildd-tools-devel mailing list