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