github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap-update-ns/common.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 main 21 22 import ( 23 "fmt" 24 25 "github.com/snapcore/snapd/cmd/snaplock" 26 "github.com/snapcore/snapd/logger" 27 "github.com/snapcore/snapd/osutil" 28 "github.com/snapcore/snapd/sandbox/cgroup" 29 ) 30 31 type CommonProfileUpdateContext struct { 32 // instanceName is the name of the snap instance to update. 33 instanceName string 34 35 // fromSnapConfine indicates that the update is triggered by snap-confine 36 // and not from snapd. When set, snap-confine is still constructing the user 37 // mount namespace and is delegating mount profile application to snap-update-ns. 38 fromSnapConfine bool 39 40 currentProfilePath string 41 desiredProfilePath string 42 } 43 44 // InstanceName returns the snap instance name being updated. 45 func (upCtx *CommonProfileUpdateContext) InstanceName() string { 46 return upCtx.instanceName 47 } 48 49 // Lock acquires locks / freezes needed to synchronize mount namespace changes. 50 func (upCtx *CommonProfileUpdateContext) Lock() (func(), error) { 51 instanceName := upCtx.instanceName 52 53 // Lock the mount namespace so that any concurrently attempted invocations 54 // of snap-confine are synchronized and will see consistent state. 55 lock, err := snaplock.OpenLock(instanceName) 56 if err != nil { 57 return nil, fmt.Errorf("cannot open lock file for mount namespace of snap %q: %s", instanceName, err) 58 } 59 60 logger.Debugf("locking mount namespace of snap %q", instanceName) 61 if upCtx.fromSnapConfine { 62 // When --from-snap-confine is passed then we just ensure that the 63 // namespace is locked. This is used by snap-confine to use 64 // snap-update-ns to apply mount profiles. 65 if err := lock.TryLock(); err != osutil.ErrAlreadyLocked { 66 // If we managed to grab the lock we should drop it. 67 lock.Close() 68 return nil, fmt.Errorf("mount namespace of snap %q is not locked but --from-snap-confine was used", instanceName) 69 } 70 } else { 71 if err := lock.Lock(); err != nil { 72 return nil, fmt.Errorf("cannot lock mount namespace of snap %q: %s", instanceName, err) 73 } 74 } 75 76 // Freeze the mount namespace and unfreeze it later. This lets us perform 77 // modifications without snap processes attempting to construct 78 // symlinks or perform other malicious activity (such as attempting to 79 // introduce a symlink that would cause us to mount something other 80 // than what we expected). 81 logger.Debugf("freezing processes of snap %q", instanceName) 82 if err := cgroup.FreezeSnapProcesses(instanceName); err != nil { 83 // If we cannot freeze the processes we should drop the lock. 84 lock.Close() 85 return nil, err 86 } 87 88 unlock := func() { 89 logger.Debugf("unlocking mount namespace of snap %q", instanceName) 90 lock.Close() 91 logger.Debugf("thawing processes of snap %q", instanceName) 92 cgroup.ThawSnapProcesses(instanceName) 93 } 94 return unlock, nil 95 } 96 97 func (upCtx *CommonProfileUpdateContext) Assumptions() *Assumptions { 98 return nil 99 } 100 101 // LoadDesiredProfile loads the desired mount profile. 102 func (upCtx *CommonProfileUpdateContext) LoadDesiredProfile() (*osutil.MountProfile, error) { 103 profile, err := osutil.LoadMountProfile(upCtx.desiredProfilePath) 104 if err != nil { 105 return nil, fmt.Errorf("cannot load desired mount profile of snap %q: %s", upCtx.instanceName, err) 106 } 107 return profile, nil 108 } 109 110 // LoadCurrentProfile loads the current mount profile. 111 func (upCtx *CommonProfileUpdateContext) LoadCurrentProfile() (*osutil.MountProfile, error) { 112 profile, err := osutil.LoadMountProfile(upCtx.currentProfilePath) 113 if err != nil { 114 return nil, fmt.Errorf("cannot load current mount profile of snap %q: %s", upCtx.instanceName, err) 115 } 116 return profile, nil 117 } 118 119 // SaveCurrentProfile saves the current mount profile. 120 func (upCtx *CommonProfileUpdateContext) SaveCurrentProfile(profile *osutil.MountProfile) error { 121 if err := profile.Save(upCtx.currentProfilePath); err != nil { 122 return fmt.Errorf("cannot save current mount profile of snap %q: %s", upCtx.instanceName, err) 123 } 124 return nil 125 }