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