release.py (4908B)
1 # See LICENSE file for copyright and license details. 2 3 """ 4 Release file functions and helpers 5 """ 6 7 from datetime import datetime # , timedelta 8 from gzip import decompress as gzip_decomp 9 from lzma import compress as lzma_comp 10 from os.path import getsize, isfile 11 from subprocess import Popen 12 13 import lib.globalvars as globalvars 14 from lib.config import (checksums, distrolabel, gpgdir, release_aliases, 15 release_keys, signingkey, signrelease, arches) 16 from lib.log import info 17 from lib.parse import parse_release_head, parse_release 18 19 20 def rewrite_release_head(headers): 21 """ 22 Rewrites the necessary headers in a Release file 23 Used to override needed values defined in config.release_aliases 24 """ 25 if headers['Suite'] in release_aliases: 26 headers['Label'] = distrolabel 27 suitename = headers['Suite'] 28 for var in release_aliases[suitename]: 29 headers[var] = release_aliases[suitename][var] 30 31 return headers 32 33 34 def write_release(oldrel, newrel, filelist, rmstr, rewrite=True): 35 """ 36 Generates a valid Release file 37 if sign=False: do not use gnupg to sign the file 38 if rewrite=True: rewrite the Release headers as defined in the config 39 40 Arguments taken: oldrel, newrel, filelist, rmstr 41 * location of the old Release file (used to take metadata) 42 * location where to write the new Release file 43 * list of files to make checksums 44 * string to remove from the path of the hashed file 45 """ 46 time1 = datetime.utcnow() 47 # time2 = datetime.utcnow() + timedelta(days=7) 48 49 prettyt1 = time1.strftime('%a, %d %b %Y %H:%M:%S UTC') 50 # prettyt2 = time2.strftime('%a, %d %b %Y %H:%M:%S UTC') 51 52 # this holds our local data in case we don't want to rehash files 53 if isfile(newrel): 54 local_rel = open(newrel).read() 55 local_rel = parse_release(local_rel) 56 57 old = open(oldrel).read() 58 new = open(newrel, 'w') 59 60 rel_cont = parse_release_head(old) 61 62 rel_cont['Date'] = prettyt1 63 # rel_cont['Valid-Until'] = prettyt2 64 65 _archlist = '' 66 for i in arches: 67 if i == 'source': 68 continue 69 if i == 'binary-all': 70 continue 71 i = i.replace('binary-', ' ') 72 _archlist += i 73 rel_cont['Architectures'] = _archlist 74 75 if rewrite: 76 rel_cont = rewrite_release_head(rel_cont) 77 78 for k in release_keys: 79 if k in rel_cont: 80 new.write('%s: %s\n' % (k, rel_cont[k])) 81 82 if globalvars.rehash: 83 rehash_release(filelist, new, rmstr) 84 else: 85 info('Reusing old checksums') 86 for csum in checksums: 87 new.write('%s:\n' % csum['name']) 88 for i, j in local_rel.items(): 89 new.write(' %s %8s %s\n' % (j[0], j[1], i)) 90 91 new.close() 92 93 if signrelease: 94 sign_release(newrel) 95 96 97 def rehash_release(_filelist, fdesc, rmstr): 98 """ 99 Calculates checksums of a given filelist and writes them to the given 100 file descriptor. Takes rmstr as the third argument, which is a string to 101 remove from the path of the hashed file when writing it to a file. 102 """ 103 info('Hashing checksums') 104 for csum in checksums: 105 fdesc.write('%s:\n' % csum['name']) 106 for i in _filelist: 107 if isfile(i): 108 cont = open(i, 'rb').read() 109 fdesc.write(' %s %8s %s\n' % (csum['f'](cont).hexdigest(), 110 getsize(i), 111 i.replace(rmstr+'/', ''))) 112 elif i.endswith('.xz') and isfile(i.replace('.xz', '.gz')): 113 xzstr = lzma_comp(open(i.replace('.xz', '.gz'), 'rb').read()) 114 fdesc.write(' %s %8s %s\n' % (csum['f'](xzstr).hexdigest(), 115 len(xzstr), 116 i.replace(rmstr+'/', ''))) 117 elif not i.endswith('.gz') and isfile(i+'.gz'): 118 uncomp = gzip_decomp(open(i+'.gz', 'rb').read()) 119 fdesc.write(' %s %8s %s\n' % (csum['f'](uncomp).hexdigest(), 120 len(uncomp), 121 i.replace(rmstr+'/', ''))) 122 return 123 124 125 def sign_release(infile): 126 """ 127 Signs both the clearsign and the detached signature of a Release file. 128 129 Takes a valid path to a release file as an argument. 130 """ 131 args = ['gpg', '-q', '--default-key', signingkey, '--batch', '--yes', 132 '--homedir', gpgdir] 133 134 clearargs = args + ['--clearsign', '-a', '-o', 135 infile.replace('Release', 'InRelease'), infile] 136 detachargs = args + ['-sb', '-o', infile+'.gpg', infile] 137 138 info('Signing Release (clearsign)') 139 cleargpg = Popen(clearargs) 140 cleargpg.wait(timeout=5) 141 142 info('Signing Release (detached sign)') 143 detachgpg = Popen(detachargs) 144 detachgpg.wait(timeout=5)