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