# 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 Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Copyright 2005 Duke University # Copyright 2007 Jonathan Dieter import os from yum import misc from yum import Errors from yum import types from urlgrabber.grabber import URLGrabError import deltarpm import prestoThread import Queue import thread def verifyDelta(fo, po, conduit, raiseError): """verifies the deltarpm is what we expect it to be raiseError = defaults to 0 - if 1 then will raise a URLGrabError if the file does not check out. otherwise it returns false for a failure, true for success""" if type(fo) is types.InstanceType: fo = fo.filename try: verifyChecksum(fo, po.returnSimple('deltachecksumtype'), po.returnSimple('deltachecksum')) except: if raiseError: raise URLGrabError(-1, 'Package does not match intended download') else: return False return True def verifyChecksum(filename, checksumType, csum): """Verify the checksum of the file versus the provided checksum""" try: filesum = misc.checksum(checksumType, filename) except Errors.MiscError, e: raise URLGrabError(-3, 'Could not perform checksum') if filesum != csum: raise URLGrabError(-1, 'Package does not match checksum') return 0 def reconstruct(conduit, po): deltalocal = po.returnSimple('deltalocalpath') retlist = "" drpm = deltarpm.DeltaRpmWrapper(conduit) try: drpm.apply(po.localpath, deltalocal) except: retlist += "Error rebuilding rpm from %s! Will download full package.\n" % os.path.basename(deltalocal) try: os.unlink(po.localpath) except: pass else: retlist += "Built %s from deltarpm\n" % (os.path.basename(po.localpath), os.path.basename(deltalocal)) # Check to see whether or not we should keep the drpms # FIXME: Is there any way to see whether or not a Boolean option was not set? if conduit.confBool('main', 'neverkeepdeltas'): delete = True elif conduit.confBool('main', 'keepdeltas'): delete = False elif conduit.getConf().keepcache != 0: delete = False else: delete = True if delete: try: os.unlink(deltalocal) except: pass return retlist def downloadPkgs(conduit, pkglist): """download list of package objects handed to you, return errors""" opts, commands = conduit.getCmdLine() errors = {} def adderror(po, msg): errors.setdefault(po, []).append(msg) # Set up thread for applying drpms queue = Queue.Queue(0) lock = thread.allocate_lock() curthread = prestoThread.ReconstructionThread(queue, lock, reconstruct) curthread.start() # Check whether drpm is already downloaded repo_cached = False remote_pkgs = [] for po in conduit.getDownloadPackages(): if hasattr(po, 'has_drpm') and po.has_drpm: po.to.pkgtup = po.to.realpkgtup local = po.returnSimple('deltalocalpath') if os.path.exists(local): cursize = os.stat(local)[6] totsize = long(po.returnSimple('deltasize')) try: verifyChecksum(local, po.returnSimple('deltachecksumtype'), po.returnSimple('deltachecksum')) except: if po.repo.p_repo.cache: repo_cached = True adderror(po, 'package fails checksum but caching is ' 'enabled for %s' % po.repo.p_repo.id) if cursize >= totsize: # otherwise keep it around for regetting os.unlink(local) else: # Deltarpm is local and good, let's put it in the rebuild thread. conduit.info(5, "using local copy of deltarpm for %s" % po) queue.put((conduit, po)) continue remote_pkgs.append(po) # Download deltarpms and send them to another thread to be rebuilt i = 0 for po in remote_pkgs: i += 1 checkfunc = (verifyDelta, (po, conduit, 1), {}) cache = po.repo.p_repo.http_caching != 'none' dirstat = os.statvfs(po.repo.deltasdir) if (dirstat.f_bavail * dirstat.f_bsize) <= long(po.size): adderror(po, 'Insufficient space in download directory %s ' 'to download' % po.repo.deltasdir) continue po.simple['reallocalpath'] = po.localpath po.localpath = po.returnSimple('deltalocalpath') po.simple['realrelativepath'] = po.returnSimple('relativepath') po.simple['relativepath'] = po.returnSimple('deltarelativepath') try: text = '(%s/%s): %s' % (i, len(remote_pkgs), os.path.basename(po.returnSimple('deltarelativepath'))) deltalocal = po.repo.p_repo.getPackage(po, checkfunc=checkfunc, text=text, cache=cache) except Errors.RepoError, e: adderror(po, str(e)) else: queue.put((conduit, po)) po.simple['deltalocalpath'] = deltalocal if errors.has_key(po): del errors[po] po.simple['relativepath'] = po.returnSimple('realrelativepath') po.localpath = po.returnSimple('reallocalpath') if po.simple.has_key('realpackagesize'): po.simple['packagesize'] = po.returnSimple('realpackagesize') del po.simple['realpackagesize'] del po.simple['realrelativepath'] del po.simple['reallocalpath'] # Check for waiting messages from building thread lock.acquire() if curthread.messages != "": conduit.info(2, curthread.messages[:-1]) curthread.messages = "" lock.release() # Tell build thread that there are no more drpms and wait for it to exit curthread.can_exit = True curthread.join() if curthread.messages != "": conduit.info(2, curthread.messages[:-1]) curthread.messages = "" return errors