[PATCH] Use standard losetup utility to get a free loop device (#846336)

Vratislav Podzimek vpodzime at redhat.com
Fri Jul 5 08:23:23 UTC 2013


There's no point in using our own utility that lacks important functionality. We
need to avoid relying on having specific loop devices available, people may use
them e.g. in kickstarts (the case described by the bug #846336).

Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
 command-stubs/losetup-stub | 70 -----------------------------------
 command-stubs/losetup-test | 91 ----------------------------------------------
 image.py                   | 29 +++++++++++----
 scripts/upd-instroot       |  3 +-
 yuminstall.py              |  9 +++--
 5 files changed, 27 insertions(+), 175 deletions(-)
 delete mode 100755 command-stubs/losetup-stub
 delete mode 100644 command-stubs/losetup-test

diff --git a/command-stubs/losetup-stub b/command-stubs/losetup-stub
deleted file mode 100755
index af9bd51..0000000
--- a/command-stubs/losetup-stub
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/python
-#
-# losetup-stub
-#
-# Copyright (C) 2007, 2011  Red Hat, Inc.  All rights reserved.
-#
-# This program 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 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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/>.
-#
-
-from optparse import OptionParser
-import os
-import sys
-
-sys.path.append('/usr/lib/anaconda')
-import isys
-
-
-def usage():
-    return 'Usage: %prog [-d] <loopdev> [file]'
-
-
-def main(prog, args):
-
-    def err(msg):
-        sys.stderr.write('%s: %s\n' % (os.path.basename(prog), msg))
-        sys.exit(1)
-
-    parser = OptionParser(usage=usage())
-    parser.add_option('-d', '--detach', action='store_true', default=False)
-
-    opts, args = parser.parse_args(args)
-
-    if opts.detach:
-        if not args:
-            err('missing operand')
-
-        for loopdev in args:
-            try:
-                isys.unlosetup(loopdev)
-            except SystemError as e:
-                err(e)
-
-    else:
-        try:
-            loopdev, image = args
-        except ValueError:
-            if len(args) > 2:
-                err("extra operand '%s'" % args[2])
-            else:
-                err('missing operand')
-        else:
-            try:
-                isys.losetup(loopdev, image)
-            except SystemError as e:
-                err(e)
-
-
-if __name__ == '__main__':
-    main(prog=sys.argv[0], args=sys.argv[1:])
diff --git a/command-stubs/losetup-test b/command-stubs/losetup-test
deleted file mode 100644
index d4f4bb9..0000000
--- a/command-stubs/losetup-test
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/python
-#
-# losetup-test
-#
-# Copyright (C) 2011  Red Hat, Inc.  All rights reserved.
-#
-# This program 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 2 of the License, or
-# (at your option) any later version.
-#
-# This program 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/>.
-#
-
-import os
-from subprocess import call, check_call, CalledProcessError
-import sys
-
-
-def loop_device_test(device, image):
-    # create the image file
-    f = open(image, 'w')
-    f.truncate(1000)
-    f.close()
-
-    try:
-        # new loop device
-        rc = call(['losetup', device, image])
-        if not rc == 0:
-            print('losetup: create new loop device test failed')
-            sys.exit(1)
-
-        try:
-            os.stat(device)
-        except OSError:
-            print('losetup: create new loop device test failed')
-            sys.exit(1)
-
-        # existing loop device
-        fail = False
-        rc = call(['losetup', device, image])
-        if rc == 0:
-            print('losetup: create existing loop device test failed')
-            fail = True
-
-        # remove loop device (cleanup)
-        rc = call(['losetup', '-d', device])
-        if not rc == 0:
-            print('losetup: remove loop device test failed')
-            sys.exit(1)
-
-        if fail:
-            sys.exit(1)
-
-    finally:
-        os.unlink(image)
-
-def wrong_usage_test():
-    try:
-        # missing all arguments
-        check_call(['losetup'])
-        # missing file
-        check_call(['losetup', '/dev/loop0'])
-        # missing loopdev
-        check_call(['losetup', '-d'])
-        # extra arguments
-        check_call(['losetup', '/dev/loop0', '/tmp/image-file', 'extra'])
-    except CalledProcessError:
-        # this is OK
-        pass
-    else:
-        print('losetup: wrong usage test failed')
-        sys.exit(1)
-
-def help_test():
-    rc = call(['losetup', '--help'])
-    if not rc == 0:
-        print('losetup: help test failed')
-        sys.exit(1)
-
-
-if __name__ == '__main__':
-    loop_device_test('/dev/loop7', '/tmp/image-file')
-    wrong_usage_test()
-    help_test()
diff --git a/image.py b/image.py
index fb2f083..2a7f20b 100644
--- a/image.py
+++ b/image.py
@@ -29,6 +29,14 @@ log = logging.getLogger("anaconda")
 
 _arch = iutil.getArch()
 
+def getFreeLoopDev():
+    loopdev = iutil.execWithCapture("losetup", ["-f"], fatal=True)
+    loopdev = loopdev.strip()
+    if not loopdev:
+        raise RuntimeError("No free loop device available")
+
+    return loopdev
+
 def findIsoImages(path, messageWindow):
     flush = os.stat(path)
     files = os.listdir(path)
@@ -40,13 +48,15 @@ def findIsoImages(path, messageWindow):
         if not isys.isIsoImage(what):
             continue
 
+        loopdev = getFreeLoopDev()
+
         try:
-            isys.losetup("/dev/loop2", what, readOnly = 1)
+            isys.losetup(loopdev, what, readOnly = 1)
         except SystemError:
             continue
 
         try:
-            isys.mount("/dev/loop2", "/mnt/cdimage", fstype = "iso9660",
+            isys.mount(loopdev, "/mnt/cdimage", fstype = "iso9660",
                        readOnly = True)
             for num in range(1, 10):
                 if os.access("/mnt/cdimage/.discinfo", os.R_OK):
@@ -99,7 +109,7 @@ def findIsoImages(path, messageWindow):
         except SystemError:
             pass
 
-        isys.unlosetup("/dev/loop2")
+        isys.unlosetup(loopdev)
 
     return discImages
 
@@ -177,11 +187,13 @@ def mountImage(isodir, tree, discnum, messageWindow, discImages={}):
     if discImages == {}:
         discImages = findIsoImages(isodir, messageWindow)
 
+    loopdev = getFreeLoopDev()
+
     while True:
         try:
             isoImage = "%s/%s" % (isodir, discImages[discnum])
-            isys.losetup("/dev/loop1", isoImage, readOnly = 1)
-            isys.mount("/dev/loop1", tree, fstype = 'iso9660', readOnly = True)
+            isys.losetup(loopdev, isoImage, readOnly = 1)
+            isys.mount(loopdev, tree, fstype = 'iso9660', readOnly = True)
             break
         except:
             ans = messageWindow(_("Missing ISO 9660 Image"),
@@ -199,7 +211,7 @@ def mountImage(isodir, tree, discnum, messageWindow, discImages={}):
             elif ans == 1:
                 discImages = findIsoImages(isodir, messageWindow)
 
-    return discImages
+    return (loopdev, discImages)
 
 # given groupset containing information about selected packages, use
 # the disc number info in the headers to come up with message describing
@@ -273,10 +285,11 @@ def scanForMedia(tree, storage):
 
     return None
 
-def umountImage(tree, currentMedia):
+def umountImage(tree, currentMedia, loopdev):
     if currentMedia is not None:
         isys.umount(tree, removeDir=False)
-        isys.unlosetup("/dev/loop1")
+        if loopdev:
+            isys.unlosetup(loopdev)
 
 def unmountCD(dev, messageWindow):
     if not dev:
diff --git a/scripts/upd-instroot b/scripts/upd-instroot
index 04f71de..0046685 100755
--- a/scripts/upd-instroot
+++ b/scripts/upd-instroot
@@ -568,6 +568,7 @@ usr/bin/humount
 usr/bin/id
 usr/bin/killall
 usr/bin/logger
+usr/bin/losetup
 usr/bin/lsattr*
 usr/bin/lshal
 usr/bin/lsscsi
@@ -1147,8 +1148,6 @@ mkdir -p $DEST/lib
 mkdir -p $DEST/firmware
 ln -snf /modules $DEST/lib/modules
 ln -snf /firmware $DEST/lib/firmware
-cp $DEST/usr/lib/anaconda/losetup-stub $DEST/usr/bin/losetup
-cp $DEST/usr/lib/anaconda/losetup-test $DEST/usr/bin/losetup-test
 cp $DEST/usr/lib/anaconda/list-harddrives-stub $DEST/usr/bin/list-harddrives
 cp $DEST/usr/lib/anaconda/list-harddrives-test $DEST/usr/bin/list-harddrives-test
 cp $DEST/usr/lib/anaconda/loadkeys-stub $DEST/usr/bin/loadkeys
diff --git a/yuminstall.py b/yuminstall.py
index ea3e496..7767060 100644
--- a/yuminstall.py
+++ b/yuminstall.py
@@ -344,6 +344,7 @@ class AnacondaYum(YumSorter):
         # Only needed for media installs.
         self.currentMedia = None
         self.mediagrabber = None
+        self._loopdev_used = None
 
         # Where is the source media mounted?  This is the directory
         # where Packages/ is located.
@@ -447,7 +448,7 @@ class AnacondaYum(YumSorter):
                         _("Unable to access the disc."))
 
     def _switchImage(self, discnum):
-        umountImage(self.tree, self.currentMedia)
+        umountImage(self.tree, self.currentMedia, self._loopdev_used)
         self.currentMedia = None
 
         # mountDirectory checks before doing anything, so it's safe to
@@ -455,9 +456,9 @@ class AnacondaYum(YumSorter):
         mountDirectory(self.anaconda.methodstr,
                        self.anaconda.intf.messageWindow)
 
-        self._discImages = mountImage(self.isodir, self.tree, discnum,
-                                      self.anaconda.intf.messageWindow,
-                                      discImages=self._discImages)
+        (self._loopdev_used, self._discImages) = mountImage(self.isodir, self.tree, discnum,
+                                                            self.anaconda.intf.messageWindow,
+                                                            discImages=self._discImages)
         self.currentMedia = discnum
 
     def configBaseURL(self):
-- 
1.7.11.7



More information about the anaconda-patches mailing list