[OE-core] [oe-commits] Richard Purdie : sstatesig/sstate: Add support for locked down sstate cache usage

Hongxu Jia hongxu.jia at windriver.com
Wed Sep 10 06:30:07 UTC 2014


On 09/09/2014 05:30 PM, Hongxu Jia wrote:
> On 09/05/2014 07:29 PM, git at opal.openembedded.org wrote:
>> Module: openembedded-core.git
>> Branch: master-next
>> Commit: a12e33a584a77df4bdd9ad6a5d1a58f4dde10317
>> URL: 
>> http://git.openembedded.org/?p=openembedded-core.git&a=commit;h=a12e33a584a77df4bdd9ad6a5d1a58f4dde10317
>>
>> Author: Richard Purdie <richard.purdie at linuxfoundation.org>
>> Date:   Fri Sep  5 10:40:02 2014 +0100
>>
>> sstatesig/sstate: Add support for locked down sstate cache usage
>>
>> I've been giving things some thought, specifically why sstate doesn't
>> get used more and why we have people requesting external toolchains. I'm
>> guessing the issue is that people don't like how often sstate can change
>> and the lack of an easy way to lock it down.
>>
>> Locking it down is actually quite easy so patch implements some basics
>> of how you can do this (for example to a specific toolchain). With an
>> addition like this to local.conf (or wherever):
>>
>> SIGGEN_LOCKEDSIGS = "\
>> gcc-cross:do_populate_sysroot:a8d91b35b98e1494957a2ddaf4598956 \
>> eglibc:do_populate_sysroot:13e8c68553dc61f9d67564f13b9b2d67 \
>> eglibc:do_packagedata:bfca0db1782c719d373f8636282596ee \
>> gcc-cross:do_packagedata:4b601ff4f67601395ee49c46701122f6 \
>> "
>>
>> the code at the end of the email will force the hashes to those values
>> for the recipes mentioned. The system would then find and use those
>> specific objects from the sstate cache instead of trying to build
>> anything.
>>
>> Obviously this is a little simplistic, you might need to put an override
>> against this to only apply those revisions for a specific architecture
>> for example. You'd also probably want to put code in the sstate hash
>> validation code to ensure it really did install these from sstate since
>> if it didn't you'd want to abort the build.
>>
>> This patch also implements support to add to bitbake -S which dumps the
>> locked sstate checksums for each task into a ready prepared include file
>> locked-sigs.inc (currently placed into cwd). There is a function,
>> bb.parse.siggen.dump_lockedsigs() which can be called to trigger the
>> same functionality from task space.
>>
>> A warning is added to sstate.bbclass through a call back into the siggen
>> class to warn if objects are not used from the locked cache. The
>> SIGGEN_ENFORCE_LOCKEDSIGS variable controls whether this is just a 
>> warning
>> or a fatal error.
>>
>> A script is provided to generate sstate directory from a locked-sigs 
>> file.
>>
>> Signed-off-by: Richard Purdie <richard.purdie at linuxfoundation.org>
>>
>> ---
>>
>>   meta/classes/sstate.bbclass |  3 ++
>>   meta/lib/oe/sstatesig.py    | 74 
>> +++++++++++++++++++++++++++++++++++++++++++++
>>   scripts/gen-lockedsig-cache | 40 ++++++++++++++++++++++++
>>   3 files changed, 117 insertions(+)
>>
>> diff --git a/meta/classes/sstate.bbclass b/meta/classes/sstate.bbclass
>> index ead829e..6316336 100644
>> --- a/meta/classes/sstate.bbclass
>> +++ b/meta/classes/sstate.bbclass
>> @@ -710,6 +710,9 @@ def sstate_checkhashes(sq_fn, sq_task, sq_hash, 
>> sq_hashfn, d):
>>               evdata['found'].append( (sq_fn[task], sq_task[task], 
>> sq_hash[task], sstatefile ) )
>>           bb.event.fire(bb.event.MetadataEvent("MissedSstate", 
>> evdata), d)
>>   +    if hasattr(bb.parse.siggen, "checkhashes"):
>> +        bb.parse.siggen.checkhashes(missed, ret, sq_fn, sq_task, 
>> sq_hash, sq_hashfn, d)
>> +
>
> Hi Richard,
>
> I have investigated and tested your patches, and found out invoking
> bb.parse.siggen.checkhashes in sstate_checkhashes didn't work,
> the ret and missed will alway be empty.
>
> Once locked-sigs.inc file generated and included, taskhash will never be
> changed which is replaced from locked-sigs.inc in get_taskhash, the ret
> and missed will alway be empty.
>
> ...
> WARNING: Using db-native do_fetch 29c5815138c74ce8188637729999e4a4
> WARNING: Using quilt-native do_fetch 43ac1a25892c6c7d16e2dd36c61405d8
> ...
> WARNING: ret []
> WARNING: missed []
> ...
>

Oh, it's my fault, you means a warning is added to sstate.bbclass through
a call back into the siggen class to warn if *objects are not used from the
locked cache*.

What I tested and wanted is a warn/error while hashes changed and using
locked sig instead.

They are two different things, sorry for the misunderstanding.

> We hope bitbake could support to add hook at BB_HASHCHECK_FUNCTION,
> so the users to customize their own sstate-cache hash checking mechanism,
> (Such as sign/verify sstate-cache with pgp/gpg mechanism for security 
> purpose)
>

As you mentioned, I could use BB_SIGNATURE_HANDLER to do that, sorry for 
the nosiy.

//Hongxu


> //Hongxu
>
>>       return ret
>>     BB_SETSCENE_DEPVALID = "setscene_depvalid"
>> diff --git a/meta/lib/oe/sstatesig.py b/meta/lib/oe/sstatesig.py
>> index 4188873..7b860c5 100644
>> --- a/meta/lib/oe/sstatesig.py
>> +++ b/meta/lib/oe/sstatesig.py
>> @@ -61,6 +61,16 @@ def sstate_rundepfilter(siggen, fn, recipename, 
>> task, dep, depname, dataCache):
>>       # Default to keep dependencies
>>       return True
>>   +def sstate_lockedsigs(d):
>> +    sigs = {}
>> +    lockedsigs = (d.getVar("SIGGEN_LOCKEDSIGS", True) or "").split()
>> +    for ls in lockedsigs:
>> +        pn, task, h = ls.split(":", 2)
>> +        if pn not in sigs:
>> +            sigs[pn] = {}
>> +        sigs[pn][task] = h
>> +    return sigs
>> +
>>   class SignatureGeneratorOEBasic(bb.siggen.SignatureGeneratorBasic):
>>       name = "OEBasic"
>>       def init_rundepcheck(self, data):
>> @@ -75,10 +85,74 @@ class 
>> SignatureGeneratorOEBasicHash(bb.siggen.SignatureGeneratorBasicHash):
>>       def init_rundepcheck(self, data):
>>           self.abisaferecipes = 
>> (data.getVar("SIGGEN_EXCLUDERECIPES_ABISAFE", True) or "").split()
>>           self.saferecipedeps = 
>> (data.getVar("SIGGEN_EXCLUDE_SAFE_RECIPE_DEPS", True) or "").split()
>> +        self.lockedsigs = sstate_lockedsigs(data)
>> +        self.lockedhashes = {}
>> +        self.lockedpnmap = {}
>>           pass
>>       def rundep_check(self, fn, recipename, task, dep, depname, 
>> dataCache = None):
>>           return sstate_rundepfilter(self, fn, recipename, task, dep, 
>> depname, dataCache)
>>   +    def get_taskdata(self):
>> +        data = super(bb.siggen.SignatureGeneratorBasicHash, 
>> self).get_taskdata()
>> +        return (data, self.lockedpnmap)
>> +
>> +    def set_taskdata(self, data):
>> +        coredata, self.lockedpnmap = data
>> +        super(bb.siggen.SignatureGeneratorBasicHash, 
>> self).set_taskdata(coredata)
>> +
>> +    def dump_sigs(self, dataCache, options):
>> +        self.dump_lockedsigs()
>> +        return super(bb.siggen.SignatureGeneratorBasicHash, 
>> self).dump_sigs(dataCache, options)
>> +
>> +    def get_taskhash(self, fn, task, deps, dataCache):
>> +        recipename = dataCache.pkg_fn[fn]
>> +        self.lockedpnmap[fn] = recipename
>> +        if recipename in self.lockedsigs:
>> +            if task in self.lockedsigs[recipename]:
>> +                k = fn + "." + task
>> +                h = self.lockedsigs[recipename][task]
>> +                self.lockedhashes[k] = h
>> +                self.taskhash[k] = h
>> +                #bb.warn("Using %s %s %s" % (recipename, task, h))
>> +                return h
>> +        h = super(bb.siggen.SignatureGeneratorBasicHash, 
>> self).get_taskhash(fn, task, deps, dataCache)
>> +        #bb.warn("%s %s %s" % (recipename, task, h))
>> +        return h
>> +
>> +    def dump_sigtask(self, fn, task, stampbase, runtime):
>> +        k = fn + "." + task
>> +        if k in self.lockedhashes:
>> +            return
>> +        super(bb.siggen.SignatureGeneratorBasicHash, 
>> self).dump_sigtask(fn, task, stampbase, runtime)
>> +
>> +    def dump_lockedsigs(self):
>> +        bb.plain("Writing locked sigs to " + os.getcwd() + 
>> "/locked-sigs.inc")
>> +        with open("locked-sigs.inc", "w") as f:
>> +            f.write('SIGGEN_LOCKEDSIGS = "\\\n')
>> +            #for fn in self.taskdeps:
>> +            for k in self.runtaskdeps:
>> +                    #k = fn + "." + task
>> +                    fn = k.rsplit(".",1)[0]
>> +                    task = k.rsplit(".",1)[1]
>> +                    if k not in self.taskhash:
>> +                        continue
>> +                    f.write("    " + self.lockedpnmap[fn] + ":" + 
>> task + ":" + self.taskhash[k] + " \\\n")
>> +            f.write('    "\n')
>> +
>> +    def checkhashes(self, missed, ret, sq_fn, sq_task, sq_hash, 
>> sq_hashfn, d):
>> +        enforce = (d.getVar("SIGGEN_ENFORCE_LOCKEDSIGS", True) or 
>> "1") == "1"
>> +        msgs = []
>> +        for task in range(len(sq_fn)):
>> +            if task not in ret:
>> +                for pn in self.lockedsigs:
>> +                    if sq_hash[task] in 
>> self.lockedsigs[pn].itervalues():
>> +                        msgs.append("Locked sig is set for %s:%s 
>> (%s) yet not in sstate cache?" % (pn, sq_task[task], sq_hash[task]))
>> +        if msgs and enforce:
>> +            bb.fatal("\n".join(msgs))
>> +        elif msgs:
>> +            bb.warn("\n".join(msgs))
>> +
>> +
>>   # Insert these classes into siggen's namespace so it can see and 
>> select them
>>   bb.siggen.SignatureGeneratorOEBasic = SignatureGeneratorOEBasic
>>   bb.siggen.SignatureGeneratorOEBasicHash = 
>> SignatureGeneratorOEBasicHash
>> diff --git a/scripts/gen-lockedsig-cache b/scripts/gen-lockedsig-cache
>> new file mode 100755
>> index 0000000..dfb282e
>> --- /dev/null
>> +++ b/scripts/gen-lockedsig-cache
>> @@ -0,0 +1,40 @@
>> +#!/usr/bin/env python
>> +#
>> +# gen-lockedsig-cache <locked-sigs.inc> <input-cachedir> 
>> <output-cachedir>
>> +#
>> +
>> +import os
>> +import sys
>> +import glob
>> +import shutil
>> +import errno
>> +
>> +def mkdir(d):
>> +    try:
>> +        os.makedirs(d)
>> +    except OSError as e:
>> +        if e.errno != errno.EEXIST:
>> +            raise e
>> +
>> +if len(sys.argv) < 3:
>> +    print("Incorrect number of arguments specified")
>> +    sys.exit(1)
>> +
>> +sigs = []
>> +with open(sys.argv[1]) as f:
>> +    for l in f.readlines():
>> +        if ":" in l:
>> +            sigs.append(l.split(":")[2].split()[0])
>> +
>> +files = set()
>> +for s in sigs:
>> +    p = sys.argv[2] + "/" + s[:2] + "/*" + s + "*"
>> +    files |= set(glob.glob(p))
>> +    p = sys.argv[2] + "/*/" + s[:2] + "/*" + s + "*"
>> +    files |= set(glob.glob(p))
>> +
>> +for f in files:
>> +    dst = f.replace(sys.argv[2], sys.argv[3])
>> +    mkdir(os.path.dirname(dst))
>> +    os.link(f, dst)
>> +
>>
>




More information about the Openembedded-core mailing list