[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[PATCH 3/4] Add 'squashfs' compression type



This adds the 'squashfs' compression type, which builds runtime images
that stay compressed in RAM. It accomplishes this by building the images
almost exactly like the Live images are built:

1) Create an empty ext4 filesystem on a large sparse file
2) Copy the runtime files into the ext4 filesystem
3) Place the ext4 image at "LiveOS/rootfs.img"
4) Create a squashfs.img which contains LiveOS/rootfs.img

To make this bootable, we need dracut's startup scripts. So before
creating the runtime image, we make a dracut initramfs.img by chrooting
into the runtime and running dracut.

Finally, we add squashfs.img to initramfs.img, along with an extra file
(/etc/cmdline) which directs dracut to use /squashfs.img as its root
device. And there we go! Easy, right?!
---
 share/ramdisk.ltmpl        |    1 +
 src/pylorax/__init__.py    |    8 +++-
 src/pylorax/constants.py   |    2 +
 src/pylorax/installtree.py |   98 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/share/ramdisk.ltmpl b/share/ramdisk.ltmpl
index a8da015..1f73a87 100644
--- a/share/ramdisk.ltmpl
+++ b/share/ramdisk.ltmpl
@@ -161,6 +161,7 @@ install "xorg-x11-fonts-ethiopic"
 install "xorg-x11-fonts-misc"
 install "xorg-x11-server-Xorg"
 install "xorg-x11-server-utils"
+install "xz"
 install "yum-langpacks"
 install "${product}-logos"
 install "${product}-release"
diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py
index d5da429..ff9bf3b 100644
--- a/src/pylorax/__init__.py
+++ b/src/pylorax/__init__.py
@@ -102,8 +102,8 @@ class Lorax(BaseLoraxClass):
         self.conf.set("yum", "skipbroken", "0")
 
         self.conf.add_section("compression")
-        self.conf.set("compression", "type", "xz")
-        self.conf.set("compression", "args", "-9")
+        self.conf.set("compression", "type", "squashfs")
+        self.conf.set("compression", "args", "-comp xz")
 
         # read the config file
         if os.path.isfile(conf_file):
@@ -293,6 +293,10 @@ class Lorax(BaseLoraxClass):
         logger.info("moving stubs")
         self.installtree.move_stubs()
 
+        if self.conf.get("compression", "type") == "squashfs":
+            # create dracut initramfs (before stuff gets shuffled/removed)
+            self.installtree.make_dracut_initramfs()
+
         # get the list of required modules
         logger.info("getting list of required modules")
         modules = [f[1:] for f in template if f[0] == "module"]
diff --git a/src/pylorax/constants.py b/src/pylorax/constants.py
index c37f0db..0946741 100644
--- a/src/pylorax/constants.py
+++ b/src/pylorax/constants.py
@@ -48,6 +48,8 @@ class LoraxRequiredCommands(dict):
         self["LOSETUP"] = "losetup"
         self["MKDOSFS"] = "mkdosfs"
         self["MKISOFS"] = "mkisofs"
+        self["MKFS_EXT4"] = "mkfs.ext4"
+        self["MKSQUASHFS"] = "mksquashfs"
         self["MODINFO"] = "modinfo"
         self["MOUNT"] = "mount"
         self["PARTED"] = "parted"
diff --git a/src/pylorax/installtree.py b/src/pylorax/installtree.py
index c3bfda4..e865cf4 100644
--- a/src/pylorax/installtree.py
+++ b/src/pylorax/installtree.py
@@ -46,6 +46,7 @@ class LoraxInstallTree(BaseLoraxClass):
         self.basearch = basearch
         self.libdir = libdir
         self.workdir = workdir
+        self.initramfs = None
 
         self.lcmds = constants.LoraxRequiredCommands()
 
@@ -512,7 +513,10 @@ class LoraxInstallTree(BaseLoraxClass):
         shutil.move(joinpaths(self.workdir, kernel.version),
                     joinpaths(self.root, "modules"))
 
-        self.make_initramfs_runtime(initrd, kernel, type, args)
+        if type == "squashfs":
+            self.make_squashfs_runtime(initrd, kernel, type, args)
+        else:
+            self.make_initramfs_runtime(initrd, kernel, type, args)
 
         # move modules out of the tree again
         logger.debug("moving modules outside initrd")
@@ -543,6 +547,98 @@ class LoraxInstallTree(BaseLoraxClass):
         logger.debug("compressing")
         rc = compressed.wait()
 
+    def make_dracut_initramfs(self):
+        outfile = "/tmp/initramfs.img" # inside the chroot
+        logger.debug("chrooting into installtree to create initramfs.img")
+        subprocess.check_call(["chroot", self.root,
+                               "/sbin/dracut", "--nomdadmconf", "--nolvmconf",
+                               "--xz", "--modules", "base dmsquash-live",
+                               outfile, self.kernels[0].version])
+        # move output file into installtree workdir
+        self.initramfs = joinpaths(self.workdir, "initramfs.img")
+        shutil.move(joinpaths(self.root, outfile), self.initramfs)
+
+    def make_squashfs_runtime(self, runtime, kernel, type, args):
+        '''This is a little complicated, but dracut wants to find a squashfs
+        image named "squashfs.img" which contains a filesystem image named
+        "LiveOS/rootfs.img".
+        Placing squashfs.img inside a cpio image and concatenating that
+        with the existing initramfs.img will make squashfs.img appear inside
+        initramfs at boot time.'''
+        # Check to be sure we have a dracut initramfs to use
+        assert self.initramfs, "make_dracut_initramfs has not been run!"
+
+        # These exact names are required by dracut
+        squashname = "squashfs.img"
+        imgname = "LiveOS/rootfs.img"
+
+        # Create fs image of installtree (2GB sparse file)
+        fsimage = joinpaths(self.workdir, "installtree.img")
+        open(fsimage, "wb").truncate(2*1024**3)
+        mountpoint = joinpaths(self.workdir, "rootfs")
+        os.mkdir(mountpoint, 0755)
+        mkfs = [self.lcmds.MKFS_EXT4, "-q", "-L", "Anaconda", "-F", fsimage]
+        logger.debug("formatting rootfs image: %s" % " ".join(mkfs))
+        subprocess.check_call(mkfs, stdout=subprocess.PIPE)
+        logger.debug("mounting rootfs image at %s", mountpoint)
+        subprocess.check_call([self.lcmds.MOUNT, "-o", "loop",
+                               fsimage, mountpoint])
+        try:
+            logger.info("copying installtree into rootfs image")
+            srcfiles = [joinpaths(self.root, f) for f in os.listdir(self.root)]
+            subprocess.check_call(["cp", "-a"] + srcfiles + [mountpoint])
+        finally:
+            logger.debug("unmounting rootfs image")
+            rc = subprocess.call([self.lcmds.UMOUNT, mountpoint])
+        if rc != 0:
+            logger.critical("umount %s failed (returncode %i)", mountpoint, rc)
+            sys.exit(rc)
+        os.rmdir(mountpoint)
+
+        # Make squashfs with rootfs image inside
+        logger.info("creating %s containing %s", squashname, imgname)
+        squashtree = joinpaths(self.workdir, "squashfs")
+        os.makedirs(joinpaths(squashtree, os.path.dirname(imgname)))
+        shutil.move(fsimage, joinpaths(squashtree, imgname))
+        squashimage = joinpaths(self.workdir, squashname)
+        cmd = [self.lcmds.MKSQUASHFS, squashtree, squashimage] + args.split()
+        subprocess.check_call(cmd)
+        shutil.rmtree(squashtree)
+
+        # Put squashimage in a new initramfs image with dracut config
+        logger.debug("creating initramfs image containing %s", squashname)
+        initramfsdir = joinpaths(self.workdir, "initramfs")
+        # write boot cmdline for dracut
+        cmdline = joinpaths(initramfsdir, "etc/cmdline")
+        os.makedirs(os.path.dirname(cmdline))
+        with open(cmdline, "wb") as fobj:
+            fobj.write("root=live:/{0}\n".format(squashname))
+        # add squashimage to new cpio image
+        shutil.move(squashimage, initramfsdir)
+        # create cpio container
+        squash_cpio = joinpaths(self.workdir, "squashfs.cpio")
+        chdir = lambda: os.chdir(initramfsdir)
+        find = subprocess.Popen([self.lcmds.FIND, "."], stdout=subprocess.PIPE,
+                                preexec_fn=chdir)
+        cpio = subprocess.Popen([self.lcmds.CPIO, "--quiet", "-c", "-o"],
+                                stdin=find.stdout,
+                                stdout=open(squash_cpio, "wb"),
+                                preexec_fn=chdir)
+        cpio.communicate()
+        shutil.rmtree(initramfsdir)
+
+        # create final image
+        logger.debug("concatenating dracut initramfs and squashfs initramfs")
+        logger.debug("initramfs.img size = %i", os.stat(self.initramfs).st_size)
+        with open(runtime.fpath, "wb") as output:
+            for f in self.initramfs, squash_cpio:
+                with open(f, "rb") as fobj:
+                    data = fobj.read(4096)
+                    while data:
+                        output.write(data)
+                        data = fobj.read(4096)
+        os.remove(self.initramfs)
+        os.remove(squash_cpio)
 
     @property
     def kernels(self):
-- 
1.7.5.2


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]