github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/seed/seed20.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 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 seed 21 22 /* ATTN this should *not* use: 23 24 * dirs package: it is passed an explicit directory to work on 25 26 * release.OnClassic: it assumes classic based on the model classic 27 option; consistency between system and model can/must be enforced 28 elsewhere 29 30 */ 31 32 import ( 33 "encoding/json" 34 "errors" 35 "fmt" 36 "os" 37 "path/filepath" 38 39 "github.com/snapcore/snapd/asserts" 40 "github.com/snapcore/snapd/asserts/snapasserts" 41 "github.com/snapcore/snapd/osutil" 42 "github.com/snapcore/snapd/seed/internal" 43 "github.com/snapcore/snapd/snap" 44 "github.com/snapcore/snapd/snap/naming" 45 "github.com/snapcore/snapd/strutil" 46 "github.com/snapcore/snapd/timings" 47 ) 48 49 type seed20 struct { 50 systemDir string 51 52 db asserts.RODatabase 53 54 model *asserts.Model 55 56 snapDeclsByID map[string]*asserts.SnapDeclaration 57 snapDeclsByName map[string]*asserts.SnapDeclaration 58 59 snapRevsByID map[string]*asserts.SnapRevision 60 61 optSnaps []*internal.Snap20 62 optSnapsIdx int 63 64 auxInfos map[string]*internal.AuxInfo20 65 66 metaFilesLoaded bool 67 68 essCache map[string]*Snap 69 70 snaps []*Snap 71 // modes holds a matching applicable modes set for each snap in snaps 72 modes [][]string 73 essentialSnapsNum int 74 } 75 76 func (s *seed20) LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error { 77 if db == nil { 78 // a db was not provided, create an internal temporary one 79 var err error 80 db, commitTo, err = newMemAssertionsDB(nil) 81 if err != nil { 82 return err 83 } 84 } 85 86 assertsDir := filepath.Join(s.systemDir, "assertions") 87 // collect assertions that are not the model 88 var declRefs []*asserts.Ref 89 var revRefs []*asserts.Ref 90 checkAssertion := func(ref *asserts.Ref) error { 91 switch ref.Type { 92 case asserts.ModelType: 93 return fmt.Errorf("system cannot have any model assertion but the one in the system model assertion file") 94 case asserts.SnapDeclarationType: 95 declRefs = append(declRefs, ref) 96 case asserts.SnapRevisionType: 97 revRefs = append(revRefs, ref) 98 } 99 return nil 100 } 101 102 batch, err := loadAssertions(assertsDir, checkAssertion) 103 if err != nil { 104 return err 105 } 106 107 refs, err := readAsserts(batch, filepath.Join(s.systemDir, "model")) 108 if err != nil { 109 return fmt.Errorf("cannot read model assertion: %v", err) 110 } 111 if len(refs) != 1 || refs[0].Type != asserts.ModelType { 112 return fmt.Errorf("system model assertion file must contain exactly the model assertion") 113 } 114 modelRef := refs[0] 115 116 if len(declRefs) != len(revRefs) { 117 return fmt.Errorf("system unexpectedly holds a different number of snap-declaration than snap-revision assertions") 118 } 119 120 // this also verifies the consistency of all of them 121 if err := commitTo(batch); err != nil { 122 return err 123 } 124 125 find := func(ref *asserts.Ref) (asserts.Assertion, error) { 126 a, err := ref.Resolve(db.Find) 127 if err != nil { 128 return nil, fmt.Errorf("internal error: cannot find just accepted assertion %v: %v", ref, err) 129 } 130 return a, nil 131 } 132 133 a, err := find(modelRef) 134 if err != nil { 135 return err 136 } 137 modelAssertion := a.(*asserts.Model) 138 139 snapDeclsByName := make(map[string]*asserts.SnapDeclaration, len(declRefs)) 140 snapDeclsByID := make(map[string]*asserts.SnapDeclaration, len(declRefs)) 141 142 for _, declRef := range declRefs { 143 a, err := find(declRef) 144 if err != nil { 145 return err 146 } 147 snapDecl := a.(*asserts.SnapDeclaration) 148 snapDeclsByID[snapDecl.SnapID()] = snapDecl 149 if snapDecl1 := snapDeclsByName[snapDecl.SnapName()]; snapDecl1 != nil { 150 return fmt.Errorf("cannot have multiple snap-declarations for the same snap-name: %s", snapDecl.SnapName()) 151 } 152 snapDeclsByName[snapDecl.SnapName()] = snapDecl 153 } 154 155 snapRevsByID := make(map[string]*asserts.SnapRevision, len(revRefs)) 156 157 for _, revRef := range revRefs { 158 a, err := find(revRef) 159 if err != nil { 160 return err 161 } 162 snapRevision := a.(*asserts.SnapRevision) 163 snapRevision1 := snapRevsByID[snapRevision.SnapID()] 164 if snapRevision1 != nil { 165 if snapRevision1.SnapRevision() != snapRevision.SnapRevision() { 166 return fmt.Errorf("cannot have multiple snap-revisions for the same snap-id: %s", snapRevision1.SnapID()) 167 } 168 } else { 169 snapRevsByID[snapRevision.SnapID()] = snapRevision 170 } 171 } 172 173 // remember db for later use 174 s.db = db 175 // remember 176 s.model = modelAssertion 177 s.snapDeclsByID = snapDeclsByID 178 s.snapDeclsByName = snapDeclsByName 179 s.snapRevsByID = snapRevsByID 180 181 return nil 182 } 183 184 func (s *seed20) Model() *asserts.Model { 185 if s.model == nil { 186 panic("internal error: model assertion unset (LoadAssertions not called)") 187 } 188 return s.model 189 } 190 191 func (s *seed20) Brand() (*asserts.Account, error) { 192 return findBrand(s, s.db) 193 } 194 195 func (s *seed20) UsesSnapdSnap() bool { 196 return true 197 } 198 199 func (s *seed20) loadOptions() error { 200 if s.model.Grade() != asserts.ModelDangerous { 201 // options.yaml is not supported for grade > dangerous 202 return nil 203 } 204 optionsFn := filepath.Join(s.systemDir, "options.yaml") 205 if !osutil.FileExists(optionsFn) { 206 // missing 207 return nil 208 } 209 options20, err := internal.ReadOptions20(optionsFn) 210 if err != nil { 211 return err 212 } 213 s.optSnaps = options20.Snaps 214 return nil 215 } 216 217 func (s *seed20) nextOptSnap(modSnap *asserts.ModelSnap) (optSnap *internal.Snap20, done bool) { 218 // we can merge model snaps and options snaps because 219 // both seed20.go and writer.go follow the order: 220 // system snap, model.EssentialSnaps(), model.SnapsWithoutEssential() 221 if s.optSnapsIdx == len(s.optSnaps) { 222 return nil, true 223 } 224 next := s.optSnaps[s.optSnapsIdx] 225 if modSnap == nil || naming.SameSnap(next, modSnap) { 226 s.optSnapsIdx++ 227 return next, false 228 } 229 return nil, false 230 } 231 232 func (s *seed20) loadAuxInfos() error { 233 auxInfoFn := filepath.Join(s.systemDir, "snaps", "aux-info.json") 234 if !osutil.FileExists(auxInfoFn) { 235 // missing 236 return nil 237 } 238 239 f, err := os.Open(auxInfoFn) 240 if err != nil { 241 return err 242 } 243 defer f.Close() 244 dec := json.NewDecoder(f) 245 if err := dec.Decode(&s.auxInfos); err != nil { 246 return fmt.Errorf("cannot decode aux-info.json: %v", err) 247 } 248 return nil 249 } 250 251 type noSnapDeclarationError struct { 252 snapRef naming.SnapRef 253 } 254 255 func (e *noSnapDeclarationError) Error() string { 256 snapID := e.snapRef.ID() 257 if snapID != "" { 258 return fmt.Sprintf("cannot find snap-declaration for snap-id: %s", snapID) 259 } 260 return fmt.Sprintf("cannot find snap-declaration for snap name: %s", e.snapRef.SnapName()) 261 } 262 263 func (s *seed20) lookupVerifiedRevision(snapRef naming.SnapRef, snapsDir string) (snapPath string, snapRev *asserts.SnapRevision, snapDecl *asserts.SnapDeclaration, err error) { 264 snapID := snapRef.ID() 265 if snapID != "" { 266 snapDecl = s.snapDeclsByID[snapID] 267 if snapDecl == nil { 268 return "", nil, nil, &noSnapDeclarationError{snapRef} 269 } 270 } else { 271 if s.model.Grade() != asserts.ModelDangerous { 272 return "", nil, nil, fmt.Errorf("all system snaps must be identified by snap-id, missing for %q", snapRef.SnapName()) 273 } 274 snapName := snapRef.SnapName() 275 snapDecl = s.snapDeclsByName[snapName] 276 if snapDecl == nil { 277 return "", nil, nil, &noSnapDeclarationError{snapRef} 278 } 279 snapID = snapDecl.SnapID() 280 } 281 282 snapRev = s.snapRevsByID[snapID] 283 if snapRev == nil { 284 return "", nil, nil, fmt.Errorf("internal error: cannot find snap-revision for snap-id: %s", snapID) 285 } 286 287 snapName := snapDecl.SnapName() 288 snapPath = filepath.Join(s.systemDir, snapsDir, fmt.Sprintf("%s_%d.snap", snapName, snapRev.SnapRevision())) 289 290 fi, err := os.Stat(snapPath) 291 if err != nil { 292 return "", nil, nil, fmt.Errorf("cannot stat snap: %v", err) 293 } 294 295 if fi.Size() != int64(snapRev.SnapSize()) { 296 return "", nil, nil, fmt.Errorf("cannot validate %q for snap %q (snap-id %q), wrong size", snapPath, snapName, snapID) 297 } 298 299 snapSHA3_384, _, err := asserts.SnapFileSHA3_384(snapPath) 300 if err != nil { 301 return "", nil, nil, err 302 } 303 304 if snapSHA3_384 != snapRev.SnapSHA3_384() { 305 return "", nil, nil, fmt.Errorf("cannot validate %q for snap %q (snap-id %q), hash mismatch with snap-revision", snapPath, snapName, snapID) 306 307 } 308 309 return snapPath, snapRev, snapDecl, nil 310 } 311 312 func (s *seed20) lookupSnap(snapRef naming.SnapRef, optSnap *internal.Snap20, channel string, snapsDir string, cache map[string]*Snap, tm timings.Measurer) (*Snap, error) { 313 snapKey := fmt.Sprintf("%s:%s", snapRef.ID(), snapRef.SnapName()) 314 seedSnap := cache[snapKey] 315 if seedSnap != nil { 316 return seedSnap, nil 317 } 318 319 if optSnap != nil && optSnap.Channel != "" { 320 channel = optSnap.Channel 321 } 322 323 var path string 324 var sideInfo *snap.SideInfo 325 if optSnap != nil && optSnap.Unasserted != "" { 326 path = filepath.Join(s.systemDir, "snaps", optSnap.Unasserted) 327 info, err := readInfo(path, nil) 328 if err != nil { 329 return nil, fmt.Errorf("cannot read unasserted snap: %v", err) 330 } 331 sideInfo = &snap.SideInfo{RealName: info.SnapName()} 332 // suppress channel 333 channel = "" 334 } else { 335 var err error 336 timings.Run(tm, "derive-side-info", fmt.Sprintf("hash and derive side info for snap %q", snapRef.SnapName()), func(nested timings.Measurer) { 337 var snapRev *asserts.SnapRevision 338 var snapDecl *asserts.SnapDeclaration 339 path, snapRev, snapDecl, err = s.lookupVerifiedRevision(snapRef, snapsDir) 340 if err == nil { 341 sideInfo = snapasserts.SideInfoFromSnapAssertions(snapDecl, snapRev) 342 } 343 }) 344 if err != nil { 345 return nil, err 346 } 347 } 348 349 // complement with aux-info.json information 350 auxInfo := s.auxInfos[sideInfo.SnapID] 351 if auxInfo != nil { 352 sideInfo.Private = auxInfo.Private 353 // TODO: consider whether to use this if we have links 354 sideInfo.EditedContact = auxInfo.Contact 355 } 356 357 seedSnap = &Snap{ 358 Path: path, 359 360 SideInfo: sideInfo, 361 362 Channel: channel, 363 } 364 365 if cache != nil { 366 cache[snapKey] = seedSnap 367 } 368 369 return seedSnap, nil 370 } 371 372 func (s *seed20) addSnap(snapRef naming.SnapRef, optSnap *internal.Snap20, modes []string, channel string, snapsDir string, cache map[string]*Snap, tm timings.Measurer) (*Snap, error) { 373 seedSnap, err := s.lookupSnap(snapRef, optSnap, channel, snapsDir, cache, tm) 374 if err != nil { 375 return nil, err 376 } 377 378 s.snaps = append(s.snaps, seedSnap) 379 s.modes = append(s.modes, modes) 380 381 return seedSnap, nil 382 } 383 384 var errFiltered = errors.New("filtered out") 385 386 func (s *seed20) addModelSnap(modelSnap *asserts.ModelSnap, essential bool, filter func(*asserts.ModelSnap) bool, cache map[string]*Snap, tm timings.Measurer) (*Snap, error) { 387 optSnap, _ := s.nextOptSnap(modelSnap) 388 if filter != nil && !filter(modelSnap) { 389 return nil, errFiltered 390 } 391 seedSnap, err := s.addSnap(modelSnap, optSnap, modelSnap.Modes, modelSnap.DefaultChannel, "../../snaps", cache, tm) 392 if err != nil { 393 return nil, err 394 } 395 396 seedSnap.Essential = essential 397 seedSnap.Required = essential || modelSnap.Presence == "required" 398 if essential { 399 seedSnap.EssentialType = snapTypeFromModel(modelSnap) 400 s.essentialSnapsNum++ 401 } 402 403 return seedSnap, nil 404 } 405 406 func (s *seed20) LoadMeta(tm timings.Measurer) error { 407 if err := s.loadEssentialMeta(nil, tm); err != nil { 408 return err 409 } 410 if err := s.loadModelRestMeta(tm); err != nil { 411 return err 412 } 413 414 // extra snaps 415 runMode := []string{"run"} 416 for { 417 optSnap, done := s.nextOptSnap(nil) 418 if done { 419 break 420 } 421 422 _, err := s.addSnap(optSnap, optSnap, runMode, "latest/stable", "snaps", nil, tm) 423 if err != nil { 424 return err 425 } 426 } 427 428 return nil 429 } 430 431 func (s *seed20) LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error { 432 filterEssential := essentialSnapTypesToModelFilter(essentialTypes) 433 434 if err := s.loadEssentialMeta(filterEssential, tm); err != nil { 435 return err 436 } 437 438 if s.essentialSnapsNum != len(essentialTypes) { 439 // did not find all the explicitly asked essential types 440 return fmt.Errorf("model does not specify all the requested essential snaps: %v", essentialTypes) 441 } 442 443 return nil 444 } 445 446 func (s *seed20) loadMetaFiles() error { 447 if s.metaFilesLoaded { 448 return nil 449 } 450 451 if err := s.loadOptions(); err != nil { 452 return err 453 } 454 455 if err := s.loadAuxInfos(); err != nil { 456 return err 457 } 458 459 s.metaFilesLoaded = true 460 return nil 461 } 462 463 func (s *seed20) resetSnaps() { 464 // setup essential snaps cache 465 if s.essCache == nil { 466 // 4 = snapd+base+kernel+gadget 467 s.essCache = make(map[string]*Snap, 4) 468 } 469 470 s.optSnapsIdx = 0 471 s.snaps = nil 472 s.modes = nil 473 s.essentialSnapsNum = 0 474 } 475 476 func (s *seed20) loadEssentialMeta(filterEssential func(*asserts.ModelSnap) bool, tm timings.Measurer) error { 477 model := s.Model() 478 479 if err := s.loadMetaFiles(); err != nil { 480 return err 481 } 482 483 s.resetSnaps() 484 485 essSnaps := model.EssentialSnaps() 486 const essential = true 487 488 // an explicit snapd is the first of all of snaps 489 if essSnaps[0].SnapType != "snapd" { 490 snapdSnap := internal.MakeSystemSnap("snapd", "latest/stable", []string{"run", "ephemeral"}) 491 if _, err := s.addModelSnap(snapdSnap, essential, filterEssential, s.essCache, tm); err != nil && err != errFiltered { 492 return err 493 } 494 } 495 496 for _, modelSnap := range essSnaps { 497 seedSnap, err := s.addModelSnap(modelSnap, essential, filterEssential, s.essCache, tm) 498 if err != nil { 499 if err == errFiltered { 500 continue 501 } 502 return err 503 } 504 if modelSnap.SnapType == "gadget" { 505 // sanity 506 info, err := readInfo(seedSnap.Path, seedSnap.SideInfo) 507 if err != nil { 508 return err 509 } 510 if info.Base != model.Base() { 511 return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", info.Base, model.Base()) 512 } 513 // TODO: when we allow extend models for classic 514 // we need to add the gadget base here 515 } 516 } 517 518 return nil 519 } 520 521 func (s *seed20) loadModelRestMeta(tm timings.Measurer) error { 522 model := s.Model() 523 524 const notEssential = false 525 for _, modelSnap := range model.SnapsWithoutEssential() { 526 _, err := s.addModelSnap(modelSnap, notEssential, nil, nil, tm) 527 if err != nil { 528 if _, ok := err.(*noSnapDeclarationError); ok && modelSnap.Presence == "optional" { 529 // skipped optional snap is ok 530 continue 531 } 532 return err 533 } 534 } 535 536 return nil 537 } 538 539 func (s *seed20) EssentialSnaps() []*Snap { 540 return s.snaps[:s.essentialSnapsNum] 541 } 542 543 func (s *seed20) ModeSnaps(mode string) ([]*Snap, error) { 544 snaps := s.snaps[s.essentialSnapsNum:] 545 modes := s.modes[s.essentialSnapsNum:] 546 nGuess := len(snaps) 547 ephemeral := mode != "run" 548 if ephemeral { 549 nGuess /= 2 550 } 551 res := make([]*Snap, 0, nGuess) 552 for i, snap := range snaps { 553 if !strutil.ListContains(modes[i], mode) { 554 if !ephemeral || !strutil.ListContains(modes[i], "ephemeral") { 555 continue 556 } 557 } 558 res = append(res, snap) 559 } 560 return res, nil 561 }