Problem
Running livecd-creator (livecd-tools-13.4.5-1.el6.x86_64 + python-imgcreate-13.4.5-1.el6.x86_64.rpm) leaves loopback mounts around after execution.
Environment
RedHat Enterprise Linux 6.5 running under Virtualbox 4.3
# lsof | grep loop # # time livecd-creator -d -v --logfile=/tmp/livecd.debug.out -c /a/infoboot.ks -f "INFO" # ... #Inserting fragment md5sums into iso image... #fragmd5 = a1223c226ed416222a53519717949fd69cfef83f3bf48e8fe5f3d46d5686 #frags = 20 #Setting supported flag to 0 # real 5m54.977s # user 5m14.839s # sys 1m32.820s # lsof | grep loop loop0 10927 root cwd DIR 8,3 4096 2 / loop0 10927 root rtd DIR 8,3 4096 2 / loop0 10927 root txt unknown /proc/10927/exe
loopback turds are left around as the unmounts fail and lazy unmounts are tried, which never resolve themselves.
This leaves a situation where only so many livecd-creators can be run before failing with ‘can’t get loop device’
I coded a workaround as follows.
/usr/lib/python2.6/site-packages/imgcreate/fs.py
The entire modified fs.py file from livecd-tools-13.4.5-1.el6.x86_64.rpm lives here.
Diff of orig version and patched version
$ diff -c fs.py fs.py.ORIG
*** fs.py 2014-03-07 09:41:35.000000000 -0700
--- fs.py.ORIG 2014-03-05 15:22:30.000000000 -0700
***************
*** 27,33 ****
import logging
import tempfile
import time
- import re
from imgcreate.errors import *
--- 27,32 ----
***************
*** 112,147 ****
def unmount(self):
if not self.mounted:
return
- self.mounted = False
-
- rc = subprocess.call(["/bin/umount", self.dest])
- if rc == 0:
- logging.debug("umount %s succeeded", self.dest )
- return
-
- logging.info("Second attempt Unmounting directory %s" % self.dest)
- logging.info("Calling fuser -ck on %s" % self.dest)
-
- if re.match( ".*yum.*", self.dest) == None: # We don't want to nuke everything associated with yum
- rc = subprocess.call(["/sbin/fuser", '-ck', self.dest])
- logging.info("fuser -ck returned %s" % rc )
-
- time.sleep(2)
rc = subprocess.call(["/bin/umount", self.dest])
- if rc == 0:
- logging.info("Second umount succeeded")
- return
-
- logging.debug("Unable to unmount %s normally, using lazy unmount" % self.dest)
- rc = subprocess.call(["/bin/umount", "-l", self.dest])
if rc != 0:
! raise MountError("Unable to unmount fs at %s" % self.dest)
! else:
! logging.debug("lazy umount succeeded on %s" % self.dest)
! print >> sys.stdout, "lazy umount succeeded on %s" % self.dest
!
class LoopbackMount:
"""LoopbackMount compatibility layer for old API"""
--- 111,128 ----
def unmount(self):
if not self.mounted:
return
rc = subprocess.call(["/bin/umount", self.dest])
if rc != 0:
! logging.debug("Unable to unmount %s normally, using lazy unmount" % self.dest)
! rc = subprocess.call(["/bin/umount", "-l", self.dest])
! if rc != 0:
! raise MountError("Unable to unmount fs at %s" % self.dest)
! else:
! logging.debug("lazy umount succeeded on %s" % self.dest)
! print >> sys.stdout, "lazy umount succeeded on %s" % self.dest
! self.mounted = False
class LoopbackMount:
"""LoopbackMount compatibility layer for old API"""
***************
*** 156,184 ****
self.diskmount.unmount()
def lounsetup(self):
! if not self.losetup:
! return
!
! rc = subprocess.call(["/sbin/losetup", "-d", self.loopdev])
! if rc == 0:
self.losetup = False
self.loopdev = None
- logging.info( "First call to losetup remove Succeeded " )
- return
-
- logging.info("Calling fuser -ck on %s" % self.loopdev)
- rc = subprocess.call(["/sbin/fuser", '-ck', self.loopdev])
- logging.info("fuser -ck returned %s" % rc )
-
- self.loopdev = None
- self.losetup = False
-
- rc = subprocess.call(["/sbin/losetup", "-d", self.loopdev])
- if rc != 0:
- logging.info("Second call to losetup -d Failed for %s", self.loopdev)
- return
- logging.info("Second call to losetup -d Succeeded for %s ", self.loopdev)
-
def loopsetup(self):
if self.losetup:
--- 137,146 ----
self.diskmount.unmount()
def lounsetup(self):
! if self.losetup:
! rc = subprocess.call(["/sbin/losetup", "-d", self.loopdev])
self.losetup = False
self.loopdev = None
def loopsetup(self):
if self.losetup:
***************
*** 404,430 ****
if rc == 0:
self.mounted = False
else:
! logging.info("Second attempt Unmounting directory %s" % self.mountdir)
! logging.info("Calling fuser -ck on %s" % self.mountdir)
! rc = subprocess.call(["/sbin/fuser", '-ck', self.mountdir])
! logging.info("fuser -ck returned %s" % rc )
!
! time.sleep(2)
!
! rc = subprocess.call(["/bin/umount", self.mountdir])
! if rc == 0:
! logging.info("Second umount succeeded")
self.mounted = False
- else:
- logging.debug("Second Unmounting directory %s failed, using lazy umount" % self.mountdir)
- print >> sys.stdout, "Second Unmounting directory %s failed, using lazy umount" %self.mountdir
- rc = subprocess.call(["/bin/umount", "-l", self.mountdir])
- if rc != 0:
- raise MountError("Unable to unmount filesystem at %s" % self.mountdir)
- else:
- logging.debug("lazy umount succeeded on %s" % self.mountdir)
- print >> sys.stdout, "lazy umount succeeded on %s" % self.mountdir
- self.mounted = False
if self.rmdir and not self.mounted:
try:
--- 366,380 ----
if rc == 0:
self.mounted = False
else:
! logging.debug("Unmounting directory %s failed, using lazy umount" % self.mountdir)
! print >> sys.stdout, "Unmounting directory %s failed, using lazy umount" %self.mountdir
! rc = subprocess.call(["/bin/umount", "-l", self.mountdir])
! if rc != 0:
! raise MountError("Unable to unmount filesystem at %s" % self.mountdir)
! else:
! logging.debug("lazy umount succeeded on %s" % self.mountdir)
! print >> sys.stdout, "lazy umount succeeded on %s" % self.mountdir
self.mounted = False
if self.rmdir and not self.mounted:
try:
The entire modified fs.py file from livecd-tools-13.4.5-1.el6.x86_64.rpm lives here.