[master 1/4] Add a verification step to Lorax.run.

dashea installerbot-noreply at redhat.com
Mon Jul 20 13:41:25 UTC 2015


From: David Shea <dshea at redhat.com>

After the cleanup step, check that everything in /usr/bin and /usr/sbin
can still run. Currently, this just checks that ELF files have
everything they need to link, and scripts have an interpreter.

Verifying is on by default but can be skipped with --noverify
---
 src/pylorax/__init__.py    | 10 ++++++++-
 src/pylorax/treebuilder.py | 52 +++++++++++++++++++++++++++++++++++++++++++++-
 src/sbin/lorax             |  4 +++-
 3 files changed, 63 insertions(+), 3 deletions(-)

diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py
index b1178d0..cbe66c3 100644
--- a/src/pylorax/__init__.py
+++ b/src/pylorax/__init__.py
@@ -156,7 +156,8 @@ def run(self, dbo, product, version, release, variant="", bugurl="",
             add_templates=None,
             add_template_vars=None,
             add_arch_templates=None,
-            add_arch_template_vars=None):
+            add_arch_template_vars=None,
+            verify=True):
 
         assert self._configured
 
@@ -289,6 +290,13 @@ def run(self, dbo, product, version, release, variant="", bugurl="",
         logger.info("cleaning unneeded files")
         rb.cleanup()
 
+        if verify:
+            logger.info("verifying the installroot")
+            if not rb.verify():
+                sys.exit(1)
+        else:
+            logger.info("Skipping verify")
+
         if self.debug:
             rb.writepkgsizes(joinpaths(logdir, "final-pkgsizes.txt"))
 
diff --git a/src/pylorax/treebuilder.py b/src/pylorax/treebuilder.py
index bea9bcb..5d4f8b7 100644
--- a/src/pylorax/treebuilder.py
+++ b/src/pylorax/treebuilder.py
@@ -23,12 +23,14 @@
 import os, re
 from os.path import basename
 from shutil import copytree, copy2
+from pathlib import Path
+import itertools
 
 from pylorax.sysutils import joinpaths, remove
 from pylorax.base import DataHolder
 from pylorax.ltmpl import LoraxTemplateRunner
 import pylorax.imgutils as imgutils
-from pylorax.executils import runcmd, runcmd_output
+from pylorax.executils import runcmd, runcmd_output, execWithCapture
 
 templatemap = {
     'i386':    'x86.tmpl',
@@ -143,6 +145,54 @@ def cleanup(self):
         '''Remove unneeded packages and files with runtime-cleanup.tmpl'''
         self._runner.run("runtime-cleanup.tmpl")
 
+    def verify(self):
+        '''Ensure that contents of the installroot can run'''
+        status = True
+
+        ELF_MAGIC = b'\x7fELF'
+
+        # Iterate over all files in /usr/bin and /usr/sbin
+        # For ELF files, gather them into a list and we'll check them all at
+        # the end. For files with a #!, check them as we go
+        elf_files = []
+        usr_bin = Path(self.vars.root + '/usr/bin')
+        usr_sbin = Path(self.vars.root + '/usr/sbin')
+        for path in (str(x) for x in itertools.chain(usr_bin.iterdir(), usr_sbin.iterdir()) \
+                     if x.is_file()):
+            with open(path, "rb") as f:
+                magic = f.read(4)
+                if magic == ELF_MAGIC:
+                    # Save the path, minus the chroot prefix
+                    elf_files.append(path[len(self.vars.root):])
+                elif magic[:2] == b'#!':
+                    # Reopen the file as text and read the first line.
+                    # Open as latin-1 so that stray 8-bit characters don't make
+                    # things blow up. We only really care about ASCII parts.
+                    with open(path, "rt", encoding="latin-1") as f_text:
+                        # Remove the #!, split on space, and take the first part
+                        shabang = f_text.readline()[2:].split()[0]
+
+                    # Does the path exist?
+                    if not os.path.exists(self.vars.root + shabang):
+                        logger.error('%s, needed by %s, does not exist', shabang, path)
+                        status = False
+
+        # Now, run ldd on all the ELF files
+        # Just run ldd once on everything so it isn't logged a million times.
+        # At least one thing in the list isn't going to be a dynamic executable,
+        # so use execWithCapture to ignore the exit code.
+        filename = ''
+        for line in execWithCapture('ldd', elf_files, root=self.vars.root,
+                log_output=False, filter_stderr=True).split('\n'):
+            if line and not line[0].isspace():
+                # New filename header, strip the : at the end and save
+                filename = line[:-1]
+            elif 'not found' in line:
+                logger.error('%s, needed by %s, not found', line.split()[0], filename)
+                status = False
+
+        return status
+
     def writepkgsizes(self, pkgsizefile):
         '''debugging data: write a big list of pkg sizes'''
         fobj = open(pkgsizefile, "w")
diff --git a/src/sbin/lorax b/src/sbin/lorax
index 93e9ed7..f92aeb9 100755
--- a/src/sbin/lorax
+++ b/src/sbin/lorax
@@ -114,6 +114,8 @@ def main(args):
     optional.add_argument("--add-arch-template-var", dest="add_arch_template_vars",
                         action="append", help="Set variable for architecture-specific image",
                         default=[])
+    optional.add_argument("--noverify", action="store_false", default=True, dest="verify",
+                        help="Do not verify the install root")
 
     # add the show version option
     parser.add_argument("-V", help="show program's version number and exit",
@@ -200,7 +202,7 @@ def main(args):
               add_template_vars=parsed_add_template_vars,
               add_arch_templates=opts.add_arch_templates,
               add_arch_template_vars=parsed_add_arch_template_vars,
-              remove_temp=True)
+              remove_temp=True, verify=opts.verify)
 
 
 def get_dnf_base_object(installroot, repositories, mirrorlists=None,


-- 
To view this commit on github, visit https://github.com/rhinstaller/lorax/commit/cefcf16dafc79b5f12c5bca389711c10e2f5af38


More information about the anaconda-patches mailing list