github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/assertstate/assertstate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 // Package assertstate implements the manager and state aspects responsible 21 // for the enforcement of assertions in the system and manages the system-wide 22 // assertion database. 23 package assertstate 24 25 import ( 26 "fmt" 27 "strings" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/asserts/snapasserts" 31 "github.com/snapcore/snapd/httputil" 32 "github.com/snapcore/snapd/logger" 33 "github.com/snapcore/snapd/overlord/snapstate" 34 "github.com/snapcore/snapd/overlord/state" 35 "github.com/snapcore/snapd/release" 36 "github.com/snapcore/snapd/snap" 37 ) 38 39 // Add the given assertion to the system assertion database. 40 func Add(s *state.State, a asserts.Assertion) error { 41 // TODO: deal together with asserts itself with (cascading) side effects of possible assertion updates 42 return cachedDB(s).Add(a) 43 } 44 45 // AddBatch adds the given assertion batch to the system assertion database. 46 func AddBatch(s *state.State, batch *asserts.Batch, opts *asserts.CommitOptions) error { 47 return batch.CommitTo(cachedDB(s), opts) 48 } 49 50 func findError(format string, ref *asserts.Ref, err error) error { 51 if asserts.IsNotFound(err) { 52 return fmt.Errorf(format, ref) 53 } else { 54 return fmt.Errorf(format+": %v", ref, err) 55 } 56 } 57 58 type RefreshAssertionsOptions struct { 59 IsAutoRefresh bool 60 } 61 62 // RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites. 63 func RefreshSnapDeclarations(s *state.State, userID int, opts *RefreshAssertionsOptions) error { 64 if opts == nil { 65 opts = &RefreshAssertionsOptions{} 66 } 67 68 deviceCtx, err := snapstate.DevicePastSeeding(s, nil) 69 if err != nil { 70 return err 71 } 72 73 snapStates, err := snapstate.All(s) 74 if err != nil { 75 return nil 76 } 77 78 err = bulkRefreshSnapDeclarations(s, snapStates, userID, deviceCtx, opts) 79 if err == nil { 80 // done 81 return nil 82 } 83 if _, ok := err.(*bulkAssertionFallbackError); !ok { 84 // not an error that indicates the server rejecting/failing 85 // the bulk request itself 86 return err 87 } 88 logger.Noticef("bulk refresh of snap-declarations failed, falling back to one-by-one assertion fetching: %v", err) 89 90 modelAs := deviceCtx.Model() 91 92 fetching := func(f asserts.Fetcher) error { 93 for instanceName, snapst := range snapStates { 94 sideInfo := snapst.CurrentSideInfo() 95 if sideInfo.SnapID == "" { 96 continue 97 } 98 if err := snapasserts.FetchSnapDeclaration(f, sideInfo.SnapID); err != nil { 99 if notRetried, ok := err.(*httputil.PersistentNetworkError); ok { 100 return notRetried 101 } 102 return fmt.Errorf("cannot refresh snap-declaration for %q: %v", instanceName, err) 103 } 104 } 105 106 // fetch store assertion if available 107 if modelAs.Store() != "" { 108 err := snapasserts.FetchStore(f, modelAs.Store()) 109 if err != nil && !asserts.IsNotFound(err) { 110 return err 111 } 112 } 113 114 return nil 115 } 116 return doFetch(s, userID, deviceCtx, fetching) 117 } 118 119 type refreshControlError struct { 120 errs []error 121 } 122 123 func (e *refreshControlError) Error() string { 124 if len(e.errs) == 1 { 125 return e.errs[0].Error() 126 } 127 l := []string{""} 128 for _, e := range e.errs { 129 l = append(l, e.Error()) 130 } 131 return fmt.Sprintf("refresh control errors:%s", strings.Join(l, "\n - ")) 132 } 133 134 // ValidateRefreshes validates the refresh candidate revisions represented by 135 // the snapInfos, looking for the needed refresh control validation assertions, 136 // it returns a validated subset in validated and a summary error if not all 137 // candidates validated. ignoreValidation is a set of snap-instance-names that 138 // should not be gated. 139 func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) (validated []*snap.Info, err error) { 140 // maps gated snap-ids to gating snap-ids 141 controlled := make(map[string][]string) 142 // maps gating snap-ids to their snap names 143 gatingNames := make(map[string]string) 144 145 db := DB(s) 146 snapStates, err := snapstate.All(s) 147 if err != nil { 148 return nil, err 149 } 150 for instanceName, snapst := range snapStates { 151 info, err := snapst.CurrentInfo() 152 if err != nil { 153 return nil, err 154 } 155 if info.SnapID == "" { 156 continue 157 } 158 gatingID := info.SnapID 159 if gatingNames[gatingID] != "" { 160 continue 161 } 162 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 163 "series": release.Series, 164 "snap-id": gatingID, 165 }) 166 if err != nil { 167 return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q: %v", instanceName, err) 168 } 169 decl := a.(*asserts.SnapDeclaration) 170 control := decl.RefreshControl() 171 if len(control) == 0 { 172 continue 173 } 174 gatingNames[gatingID] = decl.SnapName() 175 for _, gatedID := range control { 176 controlled[gatedID] = append(controlled[gatedID], gatingID) 177 } 178 } 179 180 var errs []error 181 for _, candInfo := range snapInfos { 182 if ignoreValidation[candInfo.InstanceName()] { 183 validated = append(validated, candInfo) 184 continue 185 } 186 gatedID := candInfo.SnapID 187 gating := controlled[gatedID] 188 if len(gating) == 0 { // easy case, no refresh control 189 validated = append(validated, candInfo) 190 continue 191 } 192 193 var validationRefs []*asserts.Ref 194 195 fetching := func(f asserts.Fetcher) error { 196 for _, gatingID := range gating { 197 valref := &asserts.Ref{ 198 Type: asserts.ValidationType, 199 PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()}, 200 } 201 err := f.Fetch(valref) 202 if notFound, ok := err.(*asserts.NotFoundError); ok && notFound.Type == asserts.ValidationType { 203 return fmt.Errorf("no validation by %q", gatingNames[gatingID]) 204 } 205 if err != nil { 206 return fmt.Errorf("cannot find validation by %q: %v", gatingNames[gatingID], err) 207 } 208 validationRefs = append(validationRefs, valref) 209 } 210 return nil 211 } 212 err := doFetch(s, userID, deviceCtx, fetching) 213 if err != nil { 214 errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: %v", candInfo.InstanceName(), candInfo.Revision, err)) 215 continue 216 } 217 218 var revoked *asserts.Validation 219 for _, valref := range validationRefs { 220 a, err := valref.Resolve(db.Find) 221 if err != nil { 222 return nil, findError("internal error: cannot find just fetched %v", valref, err) 223 } 224 if val := a.(*asserts.Validation); val.Revoked() { 225 revoked = val 226 break 227 } 228 } 229 if revoked != nil { 230 errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: validation by %q (id %q) revoked", candInfo.InstanceName(), candInfo.Revision, gatingNames[revoked.SnapID()], revoked.SnapID())) 231 continue 232 } 233 234 validated = append(validated, candInfo) 235 } 236 237 if errs != nil { 238 return validated, &refreshControlError{errs} 239 } 240 241 return validated, nil 242 } 243 244 // BaseDeclaration returns the base-declaration assertion with policies governing all snaps. 245 func BaseDeclaration(s *state.State) (*asserts.BaseDeclaration, error) { 246 // TODO: switch keeping this in the DB and have it revisioned/updated 247 // via the store 248 baseDecl := asserts.BuiltinBaseDeclaration() 249 if baseDecl == nil { 250 return nil, &asserts.NotFoundError{Type: asserts.BaseDeclarationType} 251 } 252 return baseDecl, nil 253 } 254 255 // SnapDeclaration returns the snap-declaration for the given snap-id if it is present in the system assertion database. 256 func SnapDeclaration(s *state.State, snapID string) (*asserts.SnapDeclaration, error) { 257 db := DB(s) 258 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 259 "series": release.Series, 260 "snap-id": snapID, 261 }) 262 if err != nil { 263 return nil, err 264 } 265 return a.(*asserts.SnapDeclaration), nil 266 } 267 268 // Publisher returns the account assertion for publisher of the given snap-id if it is present in the system assertion database. 269 func Publisher(s *state.State, snapID string) (*asserts.Account, error) { 270 db := DB(s) 271 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 272 "series": release.Series, 273 "snap-id": snapID, 274 }) 275 if err != nil { 276 return nil, err 277 } 278 snapDecl := a.(*asserts.SnapDeclaration) 279 a, err = db.Find(asserts.AccountType, map[string]string{ 280 "account-id": snapDecl.PublisherID(), 281 }) 282 if err != nil { 283 return nil, fmt.Errorf("internal error: cannot find account assertion for the publisher of snap %q: %v", snapDecl.SnapName(), err) 284 } 285 return a.(*asserts.Account), nil 286 } 287 288 // Store returns the store assertion with the given name/id if it is 289 // present in the system assertion database. 290 func Store(s *state.State, store string) (*asserts.Store, error) { 291 db := DB(s) 292 a, err := db.Find(asserts.StoreType, map[string]string{ 293 "store": store, 294 }) 295 if err != nil { 296 return nil, err 297 } 298 return a.(*asserts.Store), nil 299 } 300 301 // AutoAliases returns the explicit automatic aliases alias=>app mapping for the given installed snap. 302 func AutoAliases(s *state.State, info *snap.Info) (map[string]string, error) { 303 if info.SnapID == "" { 304 // without declaration 305 return nil, nil 306 } 307 decl, err := SnapDeclaration(s, info.SnapID) 308 if err != nil { 309 return nil, fmt.Errorf("internal error: cannot find snap-declaration for installed snap %q: %v", info.InstanceName(), err) 310 } 311 explicitAliases := decl.Aliases() 312 if len(explicitAliases) != 0 { 313 return explicitAliases, nil 314 } 315 // XXX: old header fallback, just to keep edge working while we fix the 316 // store, to remove before next release! 317 oldAutoAliases := decl.AutoAliases() 318 if len(oldAutoAliases) == 0 { 319 return nil, nil 320 } 321 res := make(map[string]string, len(oldAutoAliases)) 322 for _, alias := range oldAutoAliases { 323 app := info.LegacyAliases[alias] 324 if app == nil { 325 // not a known alias anymore or yet, skip 326 continue 327 328 } 329 res[alias] = app.Name 330 } 331 return res, nil 332 } 333 334 func delayedCrossMgrInit() { 335 // hook validation of refreshes into snapstate logic 336 snapstate.ValidateRefreshes = ValidateRefreshes 337 // hook auto refresh of assertions (snap declarations) into snapstate 338 snapstate.AutoRefreshAssertions = AutoRefreshAssertions 339 // hook retrieving auto-aliases into snapstate logic 340 snapstate.AutoAliases = AutoAliases 341 // hook the helper for getting enforced validation sets 342 snapstate.EnforcedValidationSets = EnforcedValidationSets 343 } 344 345 // AutoRefreshAssertions tries to refresh all assertions 346 func AutoRefreshAssertions(s *state.State, userID int) error { 347 opts := &RefreshAssertionsOptions{IsAutoRefresh: true} 348 if err := RefreshSnapDeclarations(s, userID, opts); err != nil { 349 return err 350 } 351 return RefreshValidationSetAssertions(s, userID, opts) 352 } 353 354 // RefreshValidationSetAssertions tries to refresh all validation set 355 // assertions. 356 func RefreshValidationSetAssertions(s *state.State, userID int, opts *RefreshAssertionsOptions) error { 357 if opts == nil { 358 opts = &RefreshAssertionsOptions{} 359 } 360 361 deviceCtx, err := snapstate.DevicePastSeeding(s, nil) 362 if err != nil { 363 return err 364 } 365 366 vsets, err := ValidationSets(s) 367 if err != nil { 368 return err 369 } 370 if len(vsets) == 0 { 371 return nil 372 } 373 374 monitorModeSets := make(map[string]*ValidationSetTracking) 375 enforceModeSets := make(map[string]*ValidationSetTracking) 376 for vk, vset := range vsets { 377 if vset.Mode == Monitor { 378 monitorModeSets[vk] = vset 379 } else { 380 enforceModeSets[vk] = vset 381 } 382 } 383 384 updateTracking := func(sets map[string]*ValidationSetTracking) error { 385 // update validation set tracking state 386 for _, vs := range sets { 387 if vs.PinnedAt == 0 { 388 headers := map[string]string{ 389 "series": release.Series, 390 "account-id": vs.AccountID, 391 "name": vs.Name, 392 } 393 db := DB(s) 394 as, err := db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 395 if err != nil { 396 return fmt.Errorf("internal error: cannot find assertion %v when refreshing validation-set assertions", headers) 397 } 398 if vs.Current != as.Sequence() { 399 vs.Current = as.Sequence() 400 UpdateValidationSet(s, vs) 401 } 402 } 403 } 404 return nil 405 } 406 407 if err := bulkRefreshValidationSetAsserts(s, monitorModeSets, nil, userID, deviceCtx, opts); err != nil { 408 return err 409 } 410 if err := updateTracking(monitorModeSets); err != nil { 411 return err 412 } 413 414 checkForConflicts := func(db *asserts.Database, bs asserts.Backstore) error { 415 vsets := snapasserts.NewValidationSets() 416 tmpDb := db.WithStackedBackstore(bs) 417 for _, vs := range enforceModeSets { 418 headers := map[string]string{ 419 "series": release.Series, 420 "account-id": vs.AccountID, 421 "name": vs.Name, 422 } 423 var err error 424 var as asserts.Assertion 425 if vs.PinnedAt > 0 { 426 headers["sequence"] = fmt.Sprintf("%d", vs.PinnedAt) 427 as, err = tmpDb.Find(asserts.ValidationSetType, headers) 428 } else { 429 as, err = tmpDb.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 430 } 431 if err != nil { 432 return fmt.Errorf("internal error: cannot find validation set assertion: %v", err) 433 } 434 435 vsass, ok := as.(*asserts.ValidationSet) 436 if !ok { 437 return fmt.Errorf("internal error: unexpected assertion type %s for %s", vsass.Type().Name, ValidationSetKey(vs.AccountID, vs.Name)) 438 } 439 if err := vsets.Add(vsass); err != nil { 440 return fmt.Errorf("internal error: cannot check validation sets conflicts: %v", err) 441 } 442 } 443 return vsets.Conflict() 444 } 445 446 if err := bulkRefreshValidationSetAsserts(s, enforceModeSets, checkForConflicts, userID, deviceCtx, opts); err != nil { 447 if _, ok := err.(*snapasserts.ValidationSetsConflictError); ok { 448 logger.Noticef("cannot refresh to conflicting validation set assertions: %v", err) 449 return nil 450 } 451 return err 452 } 453 if err := updateTracking(enforceModeSets); err != nil { 454 return err 455 } 456 457 return nil 458 } 459 460 // ResolveOptions carries extra options for ValidationSetAssertionForMonitor. 461 type ResolveOptions struct { 462 AllowLocalFallback bool 463 } 464 465 // ValidationSetAssertionForMonitor tries to fetch or refresh the validation 466 // set assertion with accountID/name/sequence (sequence is optional) using pool. 467 // If assertion cannot be fetched but exists locally and opts.AllowLocalFallback 468 // is set then the local one is returned 469 func ValidationSetAssertionForMonitor(st *state.State, accountID, name string, sequence int, pinned bool, userID int, opts *ResolveOptions) (as *asserts.ValidationSet, local bool, err error) { 470 if opts == nil { 471 opts = &ResolveOptions{} 472 } 473 deviceCtx, err := snapstate.DevicePastSeeding(st, nil) 474 if err != nil { 475 return nil, false, err 476 } 477 478 var vs asserts.Assertion 479 headers := map[string]string{ 480 "series": release.Series, 481 "account-id": accountID, 482 "name": name, 483 } 484 485 db := cachedDB(st) 486 487 // try to get existing one from db 488 if sequence > 0 { 489 headers["sequence"] = fmt.Sprintf("%d", sequence) 490 vs, err = db.Find(asserts.ValidationSetType, headers) 491 } else { 492 // find latest 493 vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, -1) 494 } 495 if err != nil && !asserts.IsNotFound(err) { 496 return nil, false, err 497 } 498 if err == nil { 499 as = vs.(*asserts.ValidationSet) 500 } 501 502 // try to resolve or update with pool 503 pool := asserts.NewPool(db, maxGroups) 504 atSeq := &asserts.AtSequence{ 505 Type: asserts.ValidationSetType, 506 SequenceKey: []string{release.Series, accountID, name}, 507 Sequence: sequence, 508 Pinned: pinned, 509 } 510 if as != nil { 511 atSeq.Revision = as.Revision() 512 } else { 513 atSeq.Revision = asserts.RevisionNotKnown 514 } 515 516 // resolve if not found locally, otherwise add for update 517 if as == nil { 518 if err := pool.AddUnresolvedSequence(atSeq, atSeq.Unique()); err != nil { 519 return nil, false, err 520 } 521 } else { 522 atSeq.Sequence = as.Sequence() 523 // found locally, try to update 524 atSeq.Revision = as.Revision() 525 if err := pool.AddSequenceToUpdate(atSeq, atSeq.Unique()); err != nil { 526 return nil, false, err 527 } 528 } 529 530 refreshOpts := &RefreshAssertionsOptions{IsAutoRefresh: false} 531 if err := resolvePoolNoFallback(st, pool, nil, userID, deviceCtx, refreshOpts); err != nil { 532 rerr, ok := err.(*resolvePoolError) 533 if ok && as != nil && opts.AllowLocalFallback { 534 if e := rerr.errors[atSeq.Unique()]; asserts.IsNotFound(e) { 535 // fallback: support the scenario of local assertion (snap ack) 536 // not available in the store. 537 return as, true, nil 538 } 539 } 540 return nil, false, err 541 } 542 543 // fetch the requested assertion again 544 if pinned { 545 vs, err = db.Find(asserts.ValidationSetType, headers) 546 } else { 547 vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 548 } 549 if err == nil { 550 as = vs.(*asserts.ValidationSet) 551 } 552 return as, false, err 553 } 554 555 // ValidationSetAssertionForEnforce tries to fetch the validation set assertion 556 // with the given accountID/name/sequence (sequence is optional) using pool and 557 // checks if it's not in conflict with existing validation sets in enforcing mode 558 // (all currently tracked validation set assertions get refreshed), and if they 559 // are valid for installed snaps. 560 func ValidationSetAssertionForEnforce(st *state.State, accountID, name string, sequence int, userID int, snaps []*snapasserts.InstalledSnap) (vs *asserts.ValidationSet, err error) { 561 deviceCtx, err := snapstate.DevicePastSeeding(st, nil) 562 if err != nil { 563 return nil, err 564 } 565 566 opts := &RefreshAssertionsOptions{IsAutoRefresh: false} 567 568 // refresh all currently tracked validation set assertions (this may or may not 569 // include the one requested by the caller). 570 if err = RefreshValidationSetAssertions(st, userID, opts); err != nil { 571 return nil, err 572 } 573 574 valsets, err := EnforcedValidationSets(st) 575 if err != nil { 576 return nil, err 577 } 578 579 getSpecificSequenceOrLatest := func(db *asserts.Database, headers map[string]string) (vs *asserts.ValidationSet, err error) { 580 var a asserts.Assertion 581 if _, ok := headers["sequence"]; ok { 582 a, err = db.Find(asserts.ValidationSetType, headers) 583 } else { 584 a, err = db.FindSequence(asserts.ValidationSetType, headers, -1, -1) 585 } 586 if err != nil { 587 return nil, err 588 } 589 vs = a.(*asserts.ValidationSet) 590 return vs, nil 591 } 592 593 // try to get existing from the db. It will be the latest one if it was 594 // tracked already and thus refreshed via RefreshValidationSetAssertions. 595 // Otherwise, it may be a local assertion that was tracked in the past and 596 // then forgotten, in which case we need to refresh it explicitly. 597 db := cachedDB(st) 598 headers := map[string]string{ 599 "series": release.Series, 600 "account-id": accountID, 601 "name": name, 602 } 603 if sequence > 0 { 604 headers["sequence"] = fmt.Sprintf("%d", sequence) 605 } 606 607 pool := asserts.NewPool(db, maxGroups) 608 atSeq := &asserts.AtSequence{ 609 Type: asserts.ValidationSetType, 610 SequenceKey: []string{release.Series, accountID, name}, 611 Sequence: sequence, 612 Revision: asserts.RevisionNotKnown, 613 Pinned: sequence > 0, 614 } 615 616 vs, err = getSpecificSequenceOrLatest(db, headers) 617 618 checkForConflicts := func() error { 619 if err := valsets.Add(vs); err != nil { 620 return fmt.Errorf("internal error: cannot check validation sets conflicts: %v", err) 621 } 622 if err := valsets.Conflict(); err != nil { 623 return err 624 } 625 if err := valsets.CheckInstalledSnaps(snaps); err != nil { 626 return err 627 } 628 return nil 629 } 630 631 // found locally 632 if err == nil { 633 // check if we were tracking it already; if not, that 634 // means we found an old assertion (it was very likely tracked in the 635 // past) and we need to update it as it wasn't covered 636 // by RefreshValidationSetAssertions. 637 var tr ValidationSetTracking 638 trerr := GetValidationSet(st, accountID, name, &tr) 639 if trerr != nil && trerr != state.ErrNoState { 640 return nil, trerr 641 } 642 // not tracked, update the assertion 643 if trerr == state.ErrNoState { 644 // update with pool 645 atSeq.Sequence = vs.Sequence() 646 atSeq.Revision = vs.Revision() 647 if err := pool.AddSequenceToUpdate(atSeq, atSeq.Unique()); err != nil { 648 return nil, err 649 } 650 } else { 651 // was already tracked, add to validation sets and check 652 if err := checkForConflicts(); err != nil { 653 return nil, err 654 } 655 return vs, nil 656 } 657 } else { 658 if !asserts.IsNotFound(err) { 659 return nil, err 660 } 661 662 // try to resolve with pool 663 if err := pool.AddUnresolvedSequence(atSeq, atSeq.Unique()); err != nil { 664 return nil, err 665 } 666 } 667 668 checkBeforeCommit := func(db *asserts.Database, bs asserts.Backstore) error { 669 tmpDb := db.WithStackedBackstore(bs) 670 // get the resolved validation set assert, add to validation sets and check 671 vs, err = getSpecificSequenceOrLatest(tmpDb, headers) 672 if err != nil { 673 return fmt.Errorf("internal error: cannot find validation set assertion: %v", err) 674 } 675 if err := checkForConflicts(); err != nil { 676 return err 677 } 678 // all fine, will be committed (along with its prerequisites if any) on 679 // return by resolvePoolNoFallback 680 return nil 681 } 682 683 if err := resolvePoolNoFallback(st, pool, checkBeforeCommit, userID, deviceCtx, opts); err != nil { 684 return nil, err 685 } 686 687 return vs, err 688 } 689 690 // TemporaryDB returns a temporary database stacked on top of the assertions 691 // database. Writing to it will not affect the assertions database. 692 func TemporaryDB(st *state.State) *asserts.Database { 693 db := cachedDB(st) 694 return db.WithStackedBackstore(asserts.NewMemoryBackstore()) 695 }