github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/snapstate/backend/setup.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2016 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 backend 21 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 27 "github.com/snapcore/snapd/boot" 28 "github.com/snapcore/snapd/cmd/snaplock/runinhibit" 29 "github.com/snapcore/snapd/osutil" 30 "github.com/snapcore/snapd/progress" 31 "github.com/snapcore/snapd/snap" 32 ) 33 34 // InstallRecord keeps a record of what installation effectively did as hints 35 // about what needs to be undone in case of failure. 36 type InstallRecord struct { 37 // TargetSnapExisted indicates that the target .snap file under /var/lib/snapd/snap already existed when the 38 // backend attempted SetupSnap() through squashfs Install() and should be kept. 39 TargetSnapExisted bool `json:"target-snap-existed,omitempty"` 40 } 41 42 // SetupSnap does prepare and mount the snap for further processing. 43 func (b Backend) SetupSnap(snapFilePath, instanceName string, sideInfo *snap.SideInfo, dev boot.Device, meter progress.Meter) (snapType snap.Type, installRecord *InstallRecord, err error) { 44 // This assumes that the snap was already verified or --dangerous was used. 45 46 s, snapf, oErr := OpenSnapFile(snapFilePath, sideInfo) 47 if oErr != nil { 48 return snapType, nil, oErr 49 } 50 51 // update instance key to what was requested 52 _, s.InstanceKey = snap.SplitInstanceName(instanceName) 53 54 instdir := s.MountDir() 55 56 defer func() { 57 if err == nil { 58 return 59 } 60 61 // this may remove the snap from /var/lib/snapd/snaps depending on installRecord 62 if e := b.RemoveSnapFiles(s, s.Type(), installRecord, dev, meter); e != nil { 63 meter.Notify(fmt.Sprintf("while trying to clean up due to previous failure: %v", e)) 64 } 65 }() 66 67 if err := os.MkdirAll(instdir, 0755); err != nil { 68 return snapType, nil, err 69 } 70 71 if s.InstanceKey != "" { 72 err := os.MkdirAll(snap.BaseDir(s.SnapName()), 0755) 73 if err != nil && !os.IsExist(err) { 74 return snapType, nil, err 75 } 76 } 77 78 // in uc20 run mode, all snaps must be on the same device 79 opts := &snap.InstallOptions{} 80 if dev.HasModeenv() && dev.RunMode() { 81 opts.MustNotCrossDevices = true 82 } 83 84 var didNothing bool 85 if didNothing, err = snapf.Install(s.MountFile(), instdir, opts); err != nil { 86 return snapType, nil, err 87 } 88 89 // generate the mount unit for the squashfs 90 if err := addMountUnit(s, b.preseed, meter); err != nil { 91 return snapType, nil, err 92 } 93 94 t := s.Type() 95 if err := boot.Kernel(s, t, dev).ExtractKernelAssets(snapf); err != nil { 96 return snapType, nil, fmt.Errorf("cannot install kernel: %s", err) 97 } 98 99 installRecord = &InstallRecord{TargetSnapExisted: didNothing} 100 return t, installRecord, nil 101 } 102 103 // RemoveSnapFiles removes the snap files from the disk after unmounting the snap. 104 func (b Backend) RemoveSnapFiles(s snap.PlaceInfo, typ snap.Type, installRecord *InstallRecord, dev boot.Device, meter progress.Meter) error { 105 mountDir := s.MountDir() 106 107 // this also ensures that the mount unit stops 108 if err := removeMountUnit(mountDir, meter); err != nil { 109 return err 110 } 111 112 if err := os.RemoveAll(mountDir); err != nil { 113 return err 114 } 115 116 // snapPath may either be a file or a (broken) symlink to a dir 117 snapPath := s.MountFile() 118 if _, err := os.Lstat(snapPath); err == nil { 119 // remove the kernel assets (if any) 120 if err := boot.Kernel(s, typ, dev).RemoveKernelAssets(); err != nil { 121 return err 122 } 123 124 // don't remove snap path if it existed before snap installation was attempted 125 // and is a symlink, which is the case with kernel/core snaps during seeding. 126 keepSeededSnap := installRecord != nil && installRecord.TargetSnapExisted && osutil.IsSymlink(snapPath) 127 if !keepSeededSnap { 128 // remove the snap 129 if err := os.RemoveAll(snapPath); err != nil { 130 return err 131 } 132 } 133 } 134 135 return nil 136 } 137 138 func (b Backend) RemoveSnapDir(s snap.PlaceInfo, hasOtherInstances bool) error { 139 mountDir := s.MountDir() 140 141 snapName, instanceKey := snap.SplitInstanceName(s.InstanceName()) 142 if instanceKey != "" { 143 // always ok to remove instance specific one, failure to remove 144 // is ok, there may be other revisions 145 os.Remove(filepath.Dir(mountDir)) 146 } 147 if !hasOtherInstances { 148 // remove only if not used by other instances of the same snap, 149 // failure to remove is ok, there may be other revisions 150 os.Remove(snap.BaseDir(snapName)) 151 } 152 return nil 153 } 154 155 // UndoSetupSnap undoes the work of SetupSnap using RemoveSnapFiles. 156 func (b Backend) UndoSetupSnap(s snap.PlaceInfo, typ snap.Type, installRecord *InstallRecord, dev boot.Device, meter progress.Meter) error { 157 return b.RemoveSnapFiles(s, typ, installRecord, dev, meter) 158 } 159 160 // RemoveSnapInhibitLock removes the file controlling inhibition of "snap run". 161 func (b Backend) RemoveSnapInhibitLock(instanceName string) error { 162 return runinhibit.RemoveLockFile(instanceName) 163 }