1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2019 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 main
import (
"github.com/snapcore/snapd/logger"
"github.com/snapcore/snapd/osutil"
)
// MountProfileUpdateContext provides the context of a mount namespace update.
// The context provides a way to synchronize the operation with other users of
// the snap system, to load and save the mount profiles and to provide the file
// system assumptions with which the mount namespace will be modified.
type MountProfileUpdateContext interface {
// Lock obtains locks appropriate for the update.
Lock() (unlock func(), err error)
// Assumptions computes filesystem assumptions under which the update shall operate.
Assumptions() *Assumptions
// LoadDesiredProfile loads the mount profile that should be constructed.
LoadDesiredProfile() (*osutil.MountProfile, error)
// LoadCurrentProfile loads the mount profile that is currently applied.
LoadCurrentProfile() (*osutil.MountProfile, error)
// SaveCurrentProfile saves the mount profile that is currently applied.
SaveCurrentProfile(*osutil.MountProfile) error
}
func executeMountProfileUpdate(upCtx MountProfileUpdateContext) error {
unlock, err := upCtx.Lock()
if err != nil {
return err
}
defer unlock()
desired, err := upCtx.LoadDesiredProfile()
if err != nil {
return err
}
currentBefore, err := upCtx.LoadCurrentProfile()
if err != nil {
return err
}
// Synthesize mount changes that were applied before for the purpose of the tmpfs detector.
as := upCtx.Assumptions()
for _, entry := range currentBefore.Entries {
as.AddChange(&Change{Action: Mount, Entry: entry})
}
// Compute the needed changes and perform each change if
// needed, collecting those that we managed to perform or that
// were performed already.
changesNeeded := NeededChanges(currentBefore, desired)
var changesMade []*Change
for _, change := range changesNeeded {
synthesised, err := change.Perform(as)
changesMade = append(changesMade, synthesised...)
if err != nil {
// We may have done something even if Perform itself has
// failed. We need to collect synthesized changes and
// store them.
origin := change.Entry.XSnapdOrigin()
if origin == "layout" || origin == "overname" {
// TODO: convert the test to a method over origin.
return err
} else if err != ErrIgnoredMissingMount {
logger.Noticef("cannot change mount namespace according to change %s: %s", change, err)
}
continue
}
changesMade = append(changesMade, change)
}
// Compute the new current profile so that it contains only changes that were made
// and save it back for next runs.
currentAfter := CurrentProfileFromChangesMade(changesMade)
return upCtx.SaveCurrentProfile(¤tAfter)
}
// CurrentProfileFromChangesMade computes a new mount profile a slice of changes.
//
// The return value collects mount profile entries from changes of type "mount"
// and "keep" while discarding the changes of type "unmount". The order in
// which entries are collected depends on their type.
//
// The sequence of changes, as produced by NeededChanges, is computed by
// looking at two mount profiles, examining the most recent change and working
// backwards. Since the most recent change is the last entry in the mount
// profile, the first change describes the last mount entry of the old mount
// profile.
//
// The "keep" changes are thus collected in reverse order - the order of their
// true appearance, while "mount" changes are collected in the order of their
// appearance as this represents the actual order of performed mount
// operations.
func CurrentProfileFromChangesMade(changes []*Change) osutil.MountProfile {
var profile osutil.MountProfile
for i := len(changes) - 1; i >= 0; i-- {
change := changes[i]
if change.Action == Keep {
profile.Entries = append(profile.Entries, change.Entry)
}
}
for _, change := range changes {
if change.Action == Mount {
profile.Entries = append(profile.Entries, change.Entry)
}
}
return profile
}
|