github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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  }