代码拉取完成,页面将自动刷新
同步操作将从 tupelo-shen/mysnapd 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2022 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package devicestate
import (
"errors"
"fmt"
"path/filepath"
"strings"
"gitee.com/mysnapcore/mysnapd/asserts"
"gitee.com/mysnapcore/mysnapd/boot"
"gitee.com/mysnapcore/mysnapd/dirs"
"gitee.com/mysnapcore/mysnapd/logger"
"gitee.com/mysnapcore/mysnapd/osutil"
"gitee.com/mysnapcore/mysnapd/overlord/snapstate"
"gitee.com/mysnapcore/mysnapd/overlord/state"
"gitee.com/mysnapcore/mysnapd/seed"
"gitee.com/mysnapcore/mysnapd/seed/seedwriter"
"gitee.com/mysnapcore/mysnapd/snap"
"gitee.com/mysnapcore/mysnapd/strutil"
)
func checkSystemRequestConflict(st *state.State, systemLabel string) error {
st.Lock()
defer st.Unlock()
var seeded bool
if err := st.Get("seeded", &seeded); err != nil && !errors.Is(err, state.ErrNoState) {
return err
}
if seeded {
// the system is fully seeded already
return nil
}
// inspect the current system which is stored in modeenv, note we are
// holding the state lock so there is no race against mark-seeded
// clearing recovery system; recovery system is not cleared when seeding
// fails
modeEnv, err := maybeReadModeenv()
if err != nil {
return err
}
if modeEnv == nil {
// non UC20 systems do not support actions, no conflict can
// happen
return nil
}
// not yet fully seeded, hold off requests for the system that is being
// seeded, but allow requests for other systems
if modeEnv.RecoverySystem == systemLabel {
return &snapstate.ChangeConflictError{
ChangeKind: "seed",
Message: "cannot request system action, system is seeding",
}
}
return nil
}
func systemFromSeed(label string, current *currentSystem) (*System, error) {
_, sys, err := loadSeedAndSystem(label, current)
return sys, err
}
func loadSeedAndSystem(label string, current *currentSystem) (seed.Seed, *System, error) {
s, err := seed.Open(dirs.SnapSeedDir, label)
if err != nil {
return nil, nil, fmt.Errorf("cannot open: %v", err)
}
if err := s.LoadAssertions(nil, nil); err != nil {
return nil, nil, fmt.Errorf("cannot load assertions for label %q: %v", label, err)
}
// get the model
model := s.Model()
brand, err := s.Brand()
if err != nil {
return nil, nil, fmt.Errorf("cannot obtain brand: %v", err)
}
system := &System{
Current: false,
Label: label,
Model: model,
Brand: brand,
Actions: defaultSystemActions,
}
if current.sameAs(system) {
system.Current = true
system.Actions = current.actions
}
return s, system, nil
}
type currentSystem struct {
*seededSystem
actions []SystemAction
}
func (c *currentSystem) sameAs(other *System) bool {
return c != nil &&
c.System == other.Label &&
c.Model == other.Model.Model() &&
c.BrandID == other.Brand.AccountID()
}
func currentSystemForMode(st *state.State, mode string) (*currentSystem, error) {
var system *seededSystem
var actions []SystemAction
var err error
switch mode {
case "run":
actions = currentSystemActions
system, err = currentSeededSystem(st)
case "install":
// there is no current system for install mode
return nil, nil
case "recover":
actions = recoverSystemActions
// recover mode uses modeenv for reference
system, err = seededSystemFromModeenv()
default:
return nil, fmt.Errorf("internal error: cannot identify current system for unsupported mode %q", mode)
}
if err != nil {
return nil, err
}
currentSys := ¤tSystem{
seededSystem: system,
actions: actions,
}
return currentSys, nil
}
func currentSeededSystem(st *state.State) (*seededSystem, error) {
st.Lock()
defer st.Unlock()
var whatseeded []seededSystem
if err := st.Get("seeded-systems", &whatseeded); err != nil {
return nil, err
}
if len(whatseeded) == 0 {
// unexpected
return nil, state.ErrNoState
}
// seeded systems are prepended to the list, so the most recently seeded
// one comes first
return &whatseeded[0], nil
}
func seededSystemFromModeenv() (*seededSystem, error) {
modeEnv, err := maybeReadModeenv()
if err != nil {
return nil, err
}
if modeEnv == nil {
return nil, fmt.Errorf("internal error: modeenv does not exist")
}
if modeEnv.RecoverySystem == "" {
return nil, fmt.Errorf("internal error: recovery system is unset")
}
system, err := systemFromSeed(modeEnv.RecoverySystem, nil)
if err != nil {
return nil, err
}
seededSys := &seededSystem{
System: modeEnv.RecoverySystem,
Model: system.Model.Model(),
BrandID: system.Model.BrandID(),
Revision: system.Model.Revision(),
Timestamp: system.Model.Timestamp(),
// SeedTime is intentionally left unset
}
return seededSys, nil
}
// getInfoFunc is expected to return for a given snap name a snap.Info for that
// snap and whether the snap is present is present. The second bit is relevant
// for non-essential snaps mentioned in the model, which if present and having
// an 'optional' presence in the model, will be added to the recovery system.
type getSnapInfoFunc func(name string) (info *snap.Info, snapIsPresent bool, err error)
// snapWriteObserveFunc is called with the recovery system directory and the
// path to a snap file being written. The snap file may be written to a location
// under the common snaps directory.
type snapWriteObserveFunc func(systemDir, where string) error
// createSystemForModelFromValidatedSnaps creates a new recovery system for the
// specified model with the specified label using the snaps in the database and
// the getInfo function.
//
// The function returns the directory of the new recovery system as well as the
// set of absolute file paths to the new snap files that were written for the
// recovery system - some snaps may be in the recovery system directory while
// others may be in the common snaps directory shared between multiple recovery
// systems on ubuntu-seed.
func createSystemForModelFromValidatedSnaps(model *asserts.Model, label string, db asserts.RODatabase, getInfo getSnapInfoFunc, observeWrite snapWriteObserveFunc) (dir string, err error) {
if model.Grade() == asserts.ModelGradeUnset {
return "", fmt.Errorf("cannot create a system for pre-UC20 model")
}
logger.Noticef("creating recovery system with label %q for %q", label, model.Model())
// TODO: should that path provided by boot package instead?
recoverySystemDirInRootDir := filepath.Join("/systems", label)
assertedSnapsDir := filepath.Join(boot.InitramfsUbuntuSeedDir, "snaps")
recoverySystemDir := filepath.Join(boot.InitramfsUbuntuSeedDir, recoverySystemDirInRootDir)
wOpts := &seedwriter.Options{
// RW mount of ubuntu-seed
SeedDir: boot.InitramfsUbuntuSeedDir,
Label: label,
}
w, err := seedwriter.New(model, wOpts)
if err != nil {
return "", err
}
optsSnaps := make([]*seedwriter.OptionsSnap, 0, len(model.RequiredWithEssentialSnaps()))
// collect all snaps that are present
modelSnaps := make(map[string]*snap.Info)
getModelSnap := func(name string, essential bool, nonEssentialPresence string) error {
kind := "essential"
if !essential {
kind = "non-essential"
if nonEssentialPresence != "" {
kind = fmt.Sprintf("non-essential but %v", nonEssentialPresence)
}
}
info, present, err := getInfo(name)
if err != nil {
return fmt.Errorf("cannot obtain %v snap information: %v", kind, err)
}
if !essential && !present && nonEssentialPresence == "optional" {
// non-essential snap which is declared as optionally
// present in the model
return nil
}
// grab those
logger.Debugf("%v snap: %v", kind, name)
if !present {
return fmt.Errorf("internal error: %v snap %q not present", kind, name)
}
if _, ok := modelSnaps[info.MountFile()]; ok {
// we've already seen this snap
return nil
}
// present locally
// TODO: for grade dangerous we could have a channel here which is not
// the model channel, handle that here
optsSnaps = append(optsSnaps, &seedwriter.OptionsSnap{
Path: info.MountFile(),
})
modelSnaps[info.MountFile()] = info
return nil
}
for _, sn := range model.EssentialSnaps() {
const essential = true
if err := getModelSnap(sn.SnapName(), essential, ""); err != nil {
return "", err
}
}
// snapd is implicitly needed
const snapdIsEssential = true
if err := getModelSnap("snapd", snapdIsEssential, ""); err != nil {
return "", err
}
for _, sn := range model.SnapsWithoutEssential() {
const essential = false
if err := getModelSnap(sn.SnapName(), essential, sn.Presence); err != nil {
return "", err
}
}
if err := w.SetOptionsSnaps(optsSnaps); err != nil {
return "", err
}
newFetcher := func(save func(asserts.Assertion) error) asserts.Fetcher {
fromDB := func(ref *asserts.Ref) (asserts.Assertion, error) {
return ref.Resolve(db.Find)
}
return asserts.NewFetcher(db, fromDB, save)
}
f, err := w.Start(db, newFetcher)
if err != nil {
return "", err
}
// past this point the system directory is present
localSnaps, err := w.LocalSnaps()
if err != nil {
return recoverySystemDir, err
}
for _, sn := range localSnaps {
info, ok := modelSnaps[sn.Path]
if !ok {
return recoverySystemDir, fmt.Errorf("internal error: no snap info for %q", sn.Path)
}
// TODO: the side info derived here can be different from what
// we have in snap.Info, but getting it this way can be
// expensive as we need to compute the hash, try to find a
// better way
_, aRefs, err := seedwriter.DeriveSideInfo(sn.Path, model, f, db)
if err != nil {
if !asserts.IsNotFound(err) {
return recoverySystemDir, err
} else if info.SnapID != "" {
// snap info from state must have come
// from the store, so it is unexpected
// if no assertions for it were found
return recoverySystemDir, fmt.Errorf("internal error: no assertions for asserted snap with ID: %v", info.SnapID)
}
}
if err := w.SetInfo(sn, info); err != nil {
return recoverySystemDir, err
}
sn.ARefs = aRefs
}
if err := w.InfoDerived(); err != nil {
return recoverySystemDir, err
}
for {
// get the list of snaps we need in this iteration
toDownload, err := w.SnapsToDownload()
if err != nil {
return recoverySystemDir, err
}
// which should be empty as all snaps should be accounted for
// already
if len(toDownload) > 0 {
which := make([]string, 0, len(toDownload))
for _, sn := range toDownload {
which = append(which, sn.SnapName())
}
return recoverySystemDir, fmt.Errorf("internal error: need to download snaps: %v", strings.Join(which, ", "))
}
complete, err := w.Downloaded()
if err != nil {
return recoverySystemDir, err
}
if complete {
logger.Debugf("snap processing for creating %q complete", label)
break
}
}
for _, warn := range w.Warnings() {
logger.Noticef("WARNING creating system %q: %s", label, warn)
}
unassertedSnaps, err := w.UnassertedSnaps()
if err != nil {
return recoverySystemDir, err
}
if len(unassertedSnaps) > 0 {
locals := make([]string, len(unassertedSnaps))
for i, sn := range unassertedSnaps {
locals[i] = sn.SnapName()
}
logger.Noticef("system %q contains unasserted snaps %s", label, strutil.Quoted(locals))
}
copySnap := func(name, src, dst string) error {
// if the destination snap is in the asserted snaps dir and already
// exists, we don't need to copy it since asserted snaps are shared
if strings.HasPrefix(dst, assertedSnapsDir+"/") && osutil.FileExists(dst) {
return nil
}
// otherwise, unasserted snaps are not shared, so even if the
// destination already exists if it is not in the asserted snaps we
// should copy it
logger.Noticef("copying new seed snap %q from %v to %v", name, src, dst)
if observeWrite != nil {
if err := observeWrite(recoverySystemDir, dst); err != nil {
return err
}
}
return osutil.CopyFile(src, dst, 0)
}
if err := w.SeedSnaps(copySnap); err != nil {
return recoverySystemDir, err
}
if err := w.WriteMeta(); err != nil {
return recoverySystemDir, err
}
bootSnaps, err := w.BootSnaps()
if err != nil {
return recoverySystemDir, err
}
bootWith := &boot.RecoverySystemBootableSet{}
for _, sn := range bootSnaps {
switch sn.Info.Type() {
case snap.TypeKernel:
bootWith.Kernel = sn.Info
bootWith.KernelPath = sn.Path
case snap.TypeGadget:
bootWith.GadgetSnapOrDir = sn.Path
}
}
if err := boot.MakeRecoverySystemBootable(boot.InitramfsUbuntuSeedDir, recoverySystemDirInRootDir, bootWith); err != nil {
return recoverySystemDir, fmt.Errorf("cannot make candidate recovery system %q bootable: %v", label, err)
}
logger.Noticef("created recovery system %q", label)
return recoverySystemDir, nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。