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