github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/seed/seedwriter/seed20.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-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 seedwriter 21 22 import ( 23 "encoding/json" 24 "errors" 25 "fmt" 26 "os" 27 "path/filepath" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/seed/internal" 31 "github.com/snapcore/snapd/snap" 32 "github.com/snapcore/snapd/snap/channel" 33 "github.com/snapcore/snapd/snap/naming" 34 ) 35 36 type policy20 struct { 37 model *asserts.Model 38 opts *Options 39 40 warningf func(format string, a ...interface{}) 41 } 42 43 var errNotAllowedExceptForDangerous = errors.New("cannot override channels, add devmode snaps, local snaps, or extra snaps with a model of grade higher than dangerous") 44 45 func (pol *policy20) checkAllowedDangerous() error { 46 if pol.model.Grade() != asserts.ModelDangerous { 47 return errNotAllowedExceptForDangerous 48 } 49 return nil 50 } 51 52 func (pol *policy20) allowsDangerousFeatures() error { 53 return pol.checkAllowedDangerous() 54 } 55 56 func (pol *policy20) checkDefaultChannel(channel.Channel) error { 57 return pol.checkAllowedDangerous() 58 } 59 60 func (pol *policy20) checkSnapChannel(ch channel.Channel, whichSnap string) error { 61 return pol.checkAllowedDangerous() 62 } 63 64 func (pol *policy20) systemSnap() *asserts.ModelSnap { 65 return internal.MakeSystemSnap("snapd", "latest/stable", []string{"run", "ephemeral"}) 66 } 67 68 func (pol *policy20) modelSnapDefaultChannel() string { 69 // We will use latest/stable as default, model that want something else 70 // will need to to speficy a default-channel 71 return "latest/stable" 72 } 73 74 func (pol *policy20) extraSnapDefaultChannel() string { 75 // We will use latest/stable as default for consistency with 76 // model snaps, this means not taking into account default-tracks 77 // by default 78 return "latest/stable" 79 } 80 81 func (pol *policy20) checkBase(info *snap.Info, modes []string, availableByMode map[string]*naming.SnapSet) error { 82 base := info.Base 83 if base == "" { 84 if info.Type() != snap.TypeGadget && info.Type() != snap.TypeApp { 85 return nil 86 } 87 base = "core" 88 } 89 90 if pol.checkAvailable(naming.Snap(base), modes, availableByMode) { 91 return nil 92 } 93 94 whichBase := fmt.Sprintf("its base %q", base) 95 if base == "core16" { 96 if pol.checkAvailable(naming.Snap("core"), modes, availableByMode) { 97 return nil 98 } 99 whichBase += ` (or "core")` 100 } 101 102 return fmt.Errorf("cannot add snap %q without also adding %s explicitly%s", info.SnapName(), whichBase, errorMsgForModesSuffix(modes)) 103 } 104 105 func (pol *policy20) checkAvailable(snapRef naming.SnapRef, modes []string, availableByMode map[string]*naming.SnapSet) bool { 106 // checks that snapRef is available in all modes 107 for _, mode := range modes { 108 byMode := availableByMode[mode] 109 if !byMode.Contains(snapRef) { 110 if mode == "run" || mode == "ephemeral" { 111 // no additional fallback for these 112 // cases: 113 // * run is not ephemeral, 114 // is covered only by run 115 // * ephemeral is only covered by ephemeral 116 return false 117 } 118 // all non-run modes (e.g. recover) are 119 // considered ephemeral, as a fallback check 120 // if the snap is listed under the ephemeral mode label 121 ephem := availableByMode["ephemeral"] 122 if ephem == nil || !ephem.Contains(snapRef) { 123 return false 124 } 125 } 126 } 127 return true 128 } 129 130 func (pol *policy20) needsImplicitSnaps(map[string]*naming.SnapSet) (bool, error) { 131 // no implicit snaps with Core 20 132 // TODO: unless we want to support them for extra snaps 133 return false, nil 134 } 135 136 func (pol *policy20) implicitSnaps(map[string]*naming.SnapSet) []*asserts.ModelSnap { 137 return nil 138 } 139 140 func (pol *policy20) implicitExtraSnaps(map[string]*naming.SnapSet) []*OptionsSnap { 141 return nil 142 } 143 144 type tree20 struct { 145 grade asserts.ModelGrade 146 opts *Options 147 148 snapsDirPath string 149 systemDir string 150 151 systemSnapsDirEnsured bool 152 } 153 154 func (tr *tree20) mkFixedDirs() error { 155 tr.snapsDirPath = filepath.Join(tr.opts.SeedDir, "snaps") 156 tr.systemDir = filepath.Join(tr.opts.SeedDir, "systems", tr.opts.Label) 157 158 if err := os.MkdirAll(tr.snapsDirPath, 0755); err != nil { 159 return err 160 } 161 162 return os.MkdirAll(tr.systemDir, 0755) 163 } 164 165 func (tr *tree20) ensureSystemSnapsDir() (string, error) { 166 snapsDir := filepath.Join(tr.systemDir, "snaps") 167 if tr.systemSnapsDirEnsured { 168 return snapsDir, nil 169 } 170 if err := os.MkdirAll(snapsDir, 0755); err != nil { 171 return "", err 172 } 173 tr.systemSnapsDirEnsured = true 174 return snapsDir, nil 175 } 176 177 func (tr *tree20) snapPath(sn *SeedSnap) (string, error) { 178 var snapsDir string 179 if sn.modelSnap != nil { 180 snapsDir = tr.snapsDirPath 181 } else { 182 // extra snap 183 var err error 184 snapsDir, err = tr.ensureSystemSnapsDir() 185 if err != nil { 186 return "", err 187 } 188 } 189 return filepath.Join(snapsDir, sn.Info.Filename()), nil 190 } 191 192 func (tr *tree20) localSnapPath(sn *SeedSnap) (string, error) { 193 sysSnapsDir, err := tr.ensureSystemSnapsDir() 194 if err != nil { 195 return "", err 196 } 197 return filepath.Join(sysSnapsDir, fmt.Sprintf("%s_%s.snap", sn.SnapName(), sn.Info.Version)), nil 198 } 199 200 func (tr *tree20) writeAssertions(db asserts.RODatabase, modelRefs []*asserts.Ref, snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error { 201 assertsDir := filepath.Join(tr.systemDir, "assertions") 202 if err := os.MkdirAll(assertsDir, 0755); err != nil { 203 return err 204 } 205 206 writeByRefs := func(fname string, refsGen func(stop <-chan struct{}) <-chan *asserts.Ref) error { 207 f, err := os.OpenFile(filepath.Join(assertsDir, fname), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) 208 if err != nil { 209 return err 210 } 211 defer f.Close() 212 213 stop := make(chan struct{}) 214 defer close(stop) 215 refs := refsGen(stop) 216 217 enc := asserts.NewEncoder(f) 218 for { 219 aRef := <-refs 220 if aRef == nil { 221 break 222 } 223 a, err := aRef.Resolve(db.Find) 224 if err != nil { 225 return fmt.Errorf("internal error: lost saved assertion") 226 } 227 if err := enc.Encode(a); err != nil { 228 return err 229 } 230 } 231 return nil 232 } 233 234 pushRef := func(refs chan<- *asserts.Ref, ref *asserts.Ref, stop <-chan struct{}) bool { 235 select { 236 case refs <- ref: 237 return true 238 case <-stop: 239 // get unstuck if we error early 240 return false 241 } 242 } 243 244 modelOnly := func(aRef *asserts.Ref) bool { return aRef.Type == asserts.ModelType } 245 excludeModel := func(aRef *asserts.Ref) bool { return aRef.Type != asserts.ModelType } 246 247 modelRefsGen := func(include func(*asserts.Ref) bool) func(stop <-chan struct{}) <-chan *asserts.Ref { 248 return func(stop <-chan struct{}) <-chan *asserts.Ref { 249 refs := make(chan *asserts.Ref) 250 go func() { 251 for _, aRef := range modelRefs { 252 if include(aRef) { 253 if !pushRef(refs, aRef, stop) { 254 return 255 } 256 } 257 } 258 close(refs) 259 }() 260 return refs 261 } 262 } 263 264 if err := writeByRefs("../model", modelRefsGen(modelOnly)); err != nil { 265 return err 266 } 267 268 if err := writeByRefs("model-etc", modelRefsGen(excludeModel)); err != nil { 269 return err 270 } 271 272 snapsRefGen := func(snaps []*SeedSnap) func(stop <-chan struct{}) <-chan *asserts.Ref { 273 return func(stop <-chan struct{}) <-chan *asserts.Ref { 274 refs := make(chan *asserts.Ref) 275 go func() { 276 for _, sn := range snaps { 277 for _, aRef := range sn.ARefs { 278 if !pushRef(refs, aRef, stop) { 279 return 280 } 281 } 282 } 283 close(refs) 284 }() 285 return refs 286 } 287 } 288 289 if err := writeByRefs("snaps", snapsRefGen(snapsFromModel)); err != nil { 290 return err 291 } 292 293 if len(extraSnaps) != 0 { 294 if err := writeByRefs("extra-snaps", snapsRefGen(extraSnaps)); err != nil { 295 return err 296 } 297 } 298 299 return nil 300 } 301 302 func (tr *tree20) writeMeta(snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error { 303 var optionsSnaps []*internal.Snap20 304 305 for _, sn := range snapsFromModel { 306 channelOverride := "" 307 if sn.Channel != sn.modelSnap.DefaultChannel { 308 channelOverride = sn.Channel 309 } 310 if sn.Info.ID() != "" && channelOverride == "" { 311 continue 312 } 313 unasserted := "" 314 if sn.Info.ID() == "" { 315 unasserted = filepath.Base(sn.Path) 316 } 317 318 optionsSnaps = append(optionsSnaps, &internal.Snap20{ 319 Name: sn.SnapName(), 320 // even if unasserted != "" SnapID is useful 321 // to cross-ref the model entry 322 SnapID: sn.modelSnap.ID(), 323 Unasserted: unasserted, 324 Channel: channelOverride, 325 }) 326 } 327 328 for _, sn := range extraSnaps { 329 channel := sn.Channel 330 unasserted := "" 331 if sn.Info.ID() == "" { 332 unasserted = filepath.Base(sn.Path) 333 channel = "" 334 } 335 336 optionsSnaps = append(optionsSnaps, &internal.Snap20{ 337 Name: sn.SnapName(), 338 SnapID: sn.Info.ID(), 339 Unasserted: unasserted, 340 Channel: channel, 341 }) 342 } 343 344 if len(optionsSnaps) != 0 { 345 if tr.grade != asserts.ModelDangerous { 346 return fmt.Errorf("internal error: unexpected non-model snap overrides with grade %s", tr.grade) 347 } 348 options20 := &internal.Options20{Snaps: optionsSnaps} 349 if err := options20.Write(filepath.Join(tr.systemDir, "options.yaml")); err != nil { 350 return err 351 } 352 } 353 354 auxInfos := make(map[string]*internal.AuxInfo20) 355 356 addAuxInfos := func(seedSnaps []*SeedSnap) { 357 for _, sn := range seedSnaps { 358 if sn.Info.ID() != "" { 359 if sn.Info.Contact != "" || sn.Info.Private { 360 auxInfos[sn.Info.ID()] = &internal.AuxInfo20{ 361 Private: sn.Info.Private, 362 Contact: sn.Info.Contact, 363 } 364 } 365 } 366 } 367 } 368 369 addAuxInfos(snapsFromModel) 370 addAuxInfos(extraSnaps) 371 372 if len(auxInfos) == 0 { 373 // nothing to do 374 return nil 375 } 376 377 if _, err := tr.ensureSystemSnapsDir(); err != nil { 378 return err 379 } 380 381 f, err := os.OpenFile(filepath.Join(tr.systemDir, "snaps", "aux-info.json"), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) 382 if err != nil { 383 return err 384 } 385 defer f.Close() 386 enc := json.NewEncoder(f) 387 388 if err := enc.Encode(auxInfos); err != nil { 389 return err 390 } 391 392 return nil 393 }