[PATCH mock] Bind-mount a precreated /dev filesystem instead of using mknod(2)

Enrico Scholz enrico.scholz at informatik.tu-chemnitz.de
Sat May 5 17:30:40 UTC 2007


This patch adds a 'dev-template' configuration options which points to a
directory with a preconfigured /dev directory. This makes it possible to
use mock in environments where 'mknod(2)' operations are prohibited (e.g.
in vservers).

Usually, this directory is a mounted read-only and e.g. a cramfs. The
'create-template.sh' script will create such a cramfs image which must be
added to the host's /etc/fstab e.g. as

| /usr/share/mock/mock-dev.ramfs /usr/share/mock/dev-template cramfs dev

and added to mock configuration as

| config_opts['dev-template'] = '/usr/share/mock/dev-template'


The changes in the spec file are suggestions only and the cramfs image 
can not be created during the normal build because it requires superuser
capabilities.

Signed-off-by: Enrico Scholz <enrico.scholz at informatik.tu-chemnitz.de>
---

 Makefile           |    2 ++
 create-template.sh |   29 ++++++++++++++++++++++
 mock-dev.ramfs     |  Bin
 mock.py            |   69 +++++++++++++++++++++++++++++-----------------------
 mock.spec          |   16 ++++++++++++
 5 files changed, 85 insertions(+), 31 deletions(-)

diff --git a/Makefile b/Makefile
index bee4ad4..a2a5d0b 100644
--- a/Makefile
+++ b/Makefile
@@ -21,6 +21,8 @@ subdirs:
 install:
 	install -D mock.py $(DESTDIR)/usr/libexec/mock.py
 	install -D mock-yum $(DESTDIR)/usr/libexec/mock-yum
+	install -D -m644 mock-dev.ramfs $(DESTDIR)/usr/share/mock/mock-dev.ramfs
+	install -d -m755 $(DESTDIR)/usr/share/mock/dev-template
 	mkdir -p $(DESTDIR)/var/lib/mock
 	for d in $(SUBDIRS); do make  DESTDIR=`cd $(DESTDIR); pwd` -C $$d install; [ $$? = 0 ] || exit 1; done
 
diff --git a/create-template.sh b/create-template.sh
new file mode 100755
index 0000000..75e2529
--- /dev/null
+++ b/create-template.sh
@@ -0,0 +1,29 @@
+#! /bin/bash
+
+MKNOD=mknod
+MKCRAMFS=/sbin/mkfs.cramfs
+
+set -e
+
+d=$(mktemp -t -d mockdev.XXXXXX)
+trap "rm -rf $d" EXIT
+
+dev=$d/dev
+mkdir -p $dev
+
+$MKNOD -m666 $dev/null    c 1 3
+$MKNOD -m644 $dev/urandom c 1 9
+$MKNOD -m644 $dev/random  c 1 8
+$MKNOD -m666 $dev/full    c 1 7
+$MKNOD -m666 $dev/ptmx    c 5 2
+$MKNOD -m666 $dev/tty     c 5 0
+$MKNOD -m666 $dev/zero    c 1 5
+
+ln -s /proc/self/fd   $dev/fd
+ln -s /proc/self/fd/0 $dev/stdin
+ln -s /proc/self/fd/1 $dev/stdout
+ln -s /proc/self/fd/2 $dev/stderr
+
+mkdir $dev/pts
+
+$MKCRAMFS $dev "$1"
diff --git a/mock-dev.ramfs b/mock-dev.ramfs
new file mode 100644
index 0000000..03851ca
Binary files /dev/null and b/mock-dev.ramfs differ
diff --git a/mock.py b/mock.py
index b06156c..3762324 100644
--- a/mock.py
+++ b/mock.py
@@ -497,6 +497,10 @@ class Root:
         # mount /proc
         self._mount('proc', 'proc', 'proc')
 
+        # mount /dev
+        if self.config.get('dev-template'):
+            self._mount('none --bind -o ro', self.config['dev-template'], 'dev')
+
         # mount /dev/pts
         self._mount('devpts', 'dev/pts', 'dev/pts')
 
@@ -536,7 +540,38 @@ class Root:
         # poof, no more file
         if os.path.exists(mf):
             os.unlink(mf)
+
+    def __mknodall(self):
+        # we need stuff
+        devices = [('null', 'c', '1', '3', '666'),
+                   ('urandom', 'c', '1', '9', '644'),
+                   ('random', 'c', '1', '9', '644'),
+                   ('full', 'c', '1', '7', '666'),
+                   ('ptmx', 'c', '5', '2', '666'),
+                   ('tty', 'c', '5', '0', '666'),
+                   ('zero', 'c', '1', '5', '666')]
+
+        for (dev, devtype, major, minor, perm) in devices:
+            devpath = os.path.join(self.rootdir, 'dev', dev)
+            cmd = '%s %s -m %s %s %s %s' % (self.config['mknod'], 
+                      devpath, perm, devtype, major, minor)
+            if not os.path.exists(devpath):
+                (retval, output) = self.do_elevated(cmd)
+                if retval != 0:
+                    raise RootError, "could not mknod error was: %s" % output      
+        
+        # link fd to ../proc/self/fd
+        devpath = os.path.join(self.rootdir, 'dev/fd')
+        if not os.path.exists(devpath):
+            os.symlink('../proc/self/fd', devpath)
         
+        fd = 0
+        for item in ('stdin', 'stdout', 'stderr'):
+            devpath =  os.path.join(self.rootdir, 'dev', item)
+            if not os.path.exists(devpath):
+                fdpath = os.path.join('../proc/self/fd', str(fd))
+                os.symlink(fdpath, devpath)
+            fd += 1
 
     def do(self, command):
         """execute given command outside of chroot"""
@@ -679,39 +714,11 @@ class Root:
                      os.path.join(self.rootdir, 'var/tmp'),
                      os.path.join(self.rootdir, 'etc/yum.repos.d')]:
             self._ensure_dir(item)
-        
-        self._mountall()
 
-        # we need stuff
-        devices = [('null', 'c', '1', '3', '666'),
-                   ('urandom', 'c', '1', '9', '644'),
-                   ('random', 'c', '1', '9', '644'),
-                   ('full', 'c', '1', '7', '666'),
-                   ('ptmx', 'c', '5', '2', '666'),
-                   ('tty', 'c', '5', '0', '666'),
-                   ('zero', 'c', '1', '5', '666')]
-
-        for (dev, devtype, major, minor, perm) in devices:
-            devpath = os.path.join(self.rootdir, 'dev', dev)
-            cmd = '%s %s -m %s %s %s %s' % (self.config['mknod'], 
-                      devpath, perm, devtype, major, minor)
-            if not os.path.exists(devpath):
-                (retval, output) = self.do_elevated(cmd)
-                if retval != 0:
-                    raise RootError, "could not mknod error was: %s" % output
+        self._mountall()
 
-        # link fd to ../proc/self/fd
-        devpath = os.path.join(self.rootdir, 'dev/fd')
-        if not os.path.exists(devpath):
-            os.symlink('../proc/self/fd', devpath)
-        
-        fd = 0
-        for item in ('stdin', 'stdout', 'stderr'):
-            devpath =  os.path.join(self.rootdir, 'dev', item)
-            if not os.path.exists(devpath):
-                fdpath = os.path.join('../proc/self/fd', str(fd))
-                os.symlink(fdpath, devpath)
-            fd += 1
+        if not self.config.get('dev-template'):
+            self.__mknodall()
 
         for item in [os.path.join(self.rootdir, 'etc', 'mtab'),
                      os.path.join(self.rootdir, 'etc', 'fstab'),
diff --git a/mock.spec b/mock.spec
index db59881..309e290 100644
--- a/mock.spec
+++ b/mock.spec
@@ -33,6 +33,7 @@ if [ -f fedora-%{fedora}-%{_target_cpu}-core.cfg ]; then
 fi
 %endif
 
+
 # if we haven't created a default link yet, try to do so as devel
 if [ ! -f default.cfg ]; then
     if [ -f fedora-development-%{_target_cpu}-core.cfg ]; then
@@ -54,6 +55,12 @@ if [ $1 -eq 1 ]; then
     groupadd -r mock >/dev/null 2>&1 || :
 fi
 
+%post
+mkdir -p -m0755 %_datadir/mock/dev-template
+
+%preun
+test $1 != 0 || rmdir %_datadir/mock/dev-template || :
+
 %files
 %defattr(-, root, root)
 %doc README ChangeLog buildsys-build.spec
@@ -65,6 +72,15 @@ fi
 %attr(02775, root, mock) %dir /var/lib/mock
 %{_libdir}/libselinux-mock.so
 
+# Do not ship the dev-template directory; it might be mounted during
+# upgrades which will cpio errors. Adding it to %%_netsharedpath is a
+# solution but requires manual editing of /etc/rpm/macros.
+#
+# Hence, create and remove this directory in scriptlets
+%dir %_datadir/mock
+%dir %_datadir/mock/*.ramfs
+%ghost %dir %_datadir/mock/dev-template
+
 %changelog
 * Mon Jan  8 2007 Clark Williams <williams at redhat.com>
 - Added Josh Boyer's EPEL config files




More information about the buildsys mailing list