github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/seed/seed.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019-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 implements loading and validating of seed data. 21 package seed 22 23 import ( 24 "errors" 25 "fmt" 26 "path/filepath" 27 28 "github.com/snapcore/snapd/asserts" 29 "github.com/snapcore/snapd/snap" 30 "github.com/snapcore/snapd/timings" 31 ) 32 33 var ( 34 ErrNoAssertions = errors.New("no seed assertions") 35 ErrNoMeta = errors.New("no seed metadata") 36 ) 37 38 // Snap holds the details of a snap in a seed. 39 type Snap struct { 40 Path string 41 42 SideInfo *snap.SideInfo 43 44 // EssentialType is the type of the snap as specified by the model. 45 // Provided only for essential snaps (Essential = true). 46 EssentialType snap.Type 47 48 Essential bool 49 Required bool 50 51 // options 52 Channel string 53 DevMode bool 54 Classic bool 55 } 56 57 func (s *Snap) SnapName() string { 58 return s.SideInfo.RealName 59 } 60 61 func (s *Snap) ID() string { 62 return s.SideInfo.SnapID 63 } 64 65 // PlaceInfo returns a PlaceInfo for the seed snap. 66 func (s *Snap) PlaceInfo() snap.PlaceInfo { 67 return &snap.Info{SideInfo: *s.SideInfo} 68 } 69 70 // Seed supports loading assertions and seed snaps' metadata. 71 type Seed interface { 72 // LoadAssertions loads all assertions from the seed with 73 // cross-checks. A read-only view on an assertions database 74 // can be passed in together with a commitTo function which 75 // will be used to commit the assertions to the underlying 76 // database. If db is nil an internal temporary database will 77 // be setup instead. ErrNoAssertions will be returned if there 78 // is no assertions directory in the seed, this is legitimate 79 // only on classic. 80 LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error 81 82 // Model returns the seed provided model assertion. 83 // It will panic if called before LoadAssertions. 84 Model() *asserts.Model 85 86 // Brand returns the brand information of the seed. 87 // It will panic if called before LoadAssertions. 88 Brand() (*asserts.Account, error) 89 90 // LoadMeta loads the seed and seed's snaps metadata while 91 // verifying the underlying snaps against assertions. It can 92 // return ErrNoMeta if there is no metadata nor snaps in the 93 // seed, this is legitimate only on classic. 94 // It will panic if called before LoadAssertions. 95 LoadMeta(tm timings.Measurer) error 96 97 // UsesSnapdSnap returns whether the system as defined by the 98 // seed will use the snapd snap, after LoadMeta. 99 UsesSnapdSnap() bool 100 101 // EssentialSnaps returns the essential snaps as defined by 102 // the seed, after LoadMeta. 103 EssentialSnaps() []*Snap 104 105 // ModeSnaps returns the snaps that should be available 106 // in the given mode as defined by the seed, after LoadMeta. 107 ModeSnaps(mode string) ([]*Snap, error) 108 } 109 110 // EssentialMetaLoaderSeed is a Seed that can be asked to load and verify 111 // only a subset of the essential model snaps via LoadEssentialMeta. 112 type EssentialMetaLoaderSeed interface { 113 Seed 114 115 // LoadEssentialMeta loads the seed's snaps metadata for the 116 // essential snaps with types in the essentialTypes set while 117 // verifying them against assertions. It can return ErrNoMeta 118 // if there is no metadata nor snaps in the seed, this is 119 // legitimate only on classic. It is an error to mix it with 120 // LoadMeta. 121 // It will panic if called before LoadAssertions. 122 LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error 123 } 124 125 // Open returns a Seed implementation for the seed at seedDir. 126 // label if not empty is used to identify a Core 20 recovery system seed. 127 func Open(seedDir, label string) (Seed, error) { 128 if label != "" { 129 if err := validateUC20SeedSystemLabel(label); err != nil { 130 return nil, err 131 } 132 return &seed20{systemDir: filepath.Join(seedDir, "systems", label)}, nil 133 } 134 // TODO: consider if systems is present to open the Core 20 135 // system if there is only one, or the lexicographically 136 // highest label one? 137 return &seed16{seedDir: seedDir}, nil 138 } 139 140 // ReadSystemEssential retrieves in one go information about the model 141 // and essential snaps of the given types for the Core 20 recovery 142 // system seed specified by seedDir and label (which cannot be empty). 143 func ReadSystemEssential(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*Snap, error) { 144 if label == "" { 145 return nil, nil, fmt.Errorf("system label cannot be empty") 146 } 147 seed, err := Open(seedDir, label) 148 if err != nil { 149 return nil, nil, err 150 } 151 152 seed20, ok := seed.(EssentialMetaLoaderSeed) 153 if !ok { 154 return nil, nil, fmt.Errorf("internal error: UC20 seed must implement EssentialMetaLoaderSeed") 155 } 156 157 // load assertions into a temporary database 158 if err := seed20.LoadAssertions(nil, nil); err != nil { 159 return nil, nil, err 160 } 161 162 // load and verify info about essential snaps 163 if err := seed20.LoadEssentialMeta(essentialTypes, tm); err != nil { 164 return nil, nil, err 165 } 166 167 return seed20.Model(), seed20.EssentialSnaps(), nil 168 }