# portage_checksum.py -- core Portage functionality # Copyright 1998-2004 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Id: /var/cvsroot/gentoo-src/portage/pym/portage_checksum.py,v 1.10.2.2 2005/08/10 05:42:03 ferringb Exp $ from portage_const import PRIVATE_PATH,PRELINK_BINARY,HASHING_BLOCKSIZE import os import errno import shutil import stat import portage_exception import portage_exec import portage_util import portage_locks import commands import sha # actual hash functions first #dict of all available hash functions hashfunc_map = {} # We _try_ to load this module. If it fails we do the slightly slower fallback. try: import fchksum def md5hash(filename): return fchksum.fmd5t(filename) except ImportError: import md5 def md5hash(filename): return pyhash(filename, md5) hashfunc_map["MD5"] = md5hash def sha1hash(filename): return pyhash(filename, sha) hashfunc_map["SHA1"] = sha1hash # Keep pycrypto optional for now, there are no internal fallbacks for these try: import Crypto.Hash.SHA256 def sha256hash(filename): return pyhash(filename, Crypto.Hash.SHA256) hashfunc_map["SHA256"] = sha256hash except ImportError: pass try: import Crypto.Hash.RIPEMD def rmd160hash(filename): return pyhash(filename, Crypto.Hash.RIPEMD) hashfunc_map["RMD160"] = rmd160hash except ImportError: pass def getsize(filename): size = os.stat(filename).st_size return (size, size) hashfunc_map["size"] = getsize # end actual hash functions prelink_capable = False if os.path.exists(PRELINK_BINARY): results = commands.getstatusoutput(PRELINK_BINARY+" --version > /dev/null 2>&1") if (results[0] >> 8) == 0: prelink_capable=1 del results def perform_md5(x, calc_prelink=0): return perform_checksum(x, "MD5", calc_prelink)[0] def perform_all(x, calc_prelink=0): mydict = {} for k in hashfunc_map.keys(): mydict[k] = perform_checksum(x, hashfunc_map[k], calc_prelink)[0] return mydict def get_valid_checksum_keys(): return hashfunc_map.keys() def verify_all(filename, mydict, calc_prelink=0, strict=0): # Dict relates to single file only. # returns: (passed,reason) file_is_ok = True reason = "Reason unknown" try: mysize = os.stat(filename)[stat.ST_SIZE] if mydict["size"] != mysize: return False,("Filesize does not match recorded size", mysize, mydict["size"]) except OSError, e: if e.errno == errno.ENOENT: raise portage_exception.FileNotFound(filename) return False, (str(e), None, None) for x in mydict.keys(): if x == "size": continue elif x in hashfunc_map.keys(): myhash = perform_checksum(filename, x, calc_prelink=calc_prelink)[0] if mydict[x] != myhash: if strict: raise portage_exception.DigestException, "Failed to verify '$(file)s' on checksum type '%(type)s'" % {"file":filename, "type":x} else: file_is_ok = False reason = (("Failed on %s verification" % x), myhash,mydict[x]) break return file_is_ok,reason def pyhash(filename, hashobject): f = open(filename, 'rb') blocksize = HASHING_BLOCKSIZE data = f.read(blocksize) size = 0L sum = hashobject.new() while data: sum.update(data) size = size + len(data) data = f.read(blocksize) f.close() return (sum.hexdigest(), size) def perform_checksum(filename, hashname="MD5", calc_prelink=0): myfilename = filename[:] prelink_tmpfile = os.path.join("/", PRIVATE_PATH, "prelink-checksum.tmp." + str(os.getpid())) mylock = None if calc_prelink and prelink_capable: mylock = portage_locks.lockfile(prelink_tmpfile, wantnewlockfile=1) # Create non-prelinked temporary file to checksum. # Files rejected by prelink are summed in place. retval=portage_exec.spawn([PRELINK_BINARY,"--undo","-o",prelink_tmpfile,filename],fd_pipes={}) if retval==0: #portage_util.writemsg(">>> prelink checksum '"+str(filename)+"'.\n") myfilename=prelink_tmpfile try: if hashname not in hashfunc_map: raise portage_exception.DigestException, hashname+" hash function not available (needs dev-python/pycrypto)" myhash, mysize = hashfunc_map[hashname](myfilename) except (OSError, IOError), e: if e.errno == errno.ENOENT: raise portage_exception.FileNotFound(myfilename) else: raise e if calc_prelink and prelink_capable: try: os.unlink(prelink_tmpfile) except OSError, oe: if oe.errno == errno.ENOENT: pass else: raise oe portage_locks.unlockfile(mylock) return (myhash,mysize) def perform_multiple_checksums(filename, hashes=["MD5"], calc_prelink=0): rVal = {} for x in hashes: if x not in hashfunc_map: raise portage_exception.DigestException, x+" hash function not available (needs dev-python/pycrypto)" rVal[x] = perform_checksum(filename, x, calc_prelink)[0] return rVal