github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/cmd/snap-preseed/reset.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 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 "io/ioutil" 25 "os" 26 "path/filepath" 27 28 "github.com/snapcore/snapd/cmd/snaplock/runinhibit" 29 "github.com/snapcore/snapd/dirs" 30 "github.com/snapcore/snapd/osutil" 31 apparmor_sandbox "github.com/snapcore/snapd/sandbox/apparmor" 32 ) 33 34 func resetPreseededChroot(preseedChroot string) error { 35 exists, isDir, err := osutil.DirExists(preseedChroot) 36 if err != nil { 37 return fmt.Errorf("cannot reset %q: %v", preseedChroot, err) 38 } 39 if !exists { 40 return fmt.Errorf("cannot reset non-existing directory %q", preseedChroot) 41 } 42 if !isDir { 43 return fmt.Errorf("cannot reset %q, it is not a directory", preseedChroot) 44 } 45 46 // globs that yield individual files 47 globs := []string{ 48 dirs.SnapStateFile, 49 dirs.SnapSystemKeyFile, 50 filepath.Join(dirs.SnapBlobDir, "*.snap"), 51 filepath.Join(dirs.SnapUdevRulesDir, "*-snap.*.rules"), 52 filepath.Join(dirs.SnapDBusSystemPolicyDir, "snap.*.*.conf"), 53 filepath.Join(dirs.SnapServicesDir, "snap.*.service"), 54 filepath.Join(dirs.SnapServicesDir, "snap.*.timer"), 55 filepath.Join(dirs.SnapServicesDir, "snap.*.socket"), 56 filepath.Join(dirs.SnapServicesDir, "snap-*.mount"), 57 filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", "snap-*.mount"), 58 filepath.Join(dirs.SnapUserServicesDir, "snap.*.service"), 59 filepath.Join(dirs.SnapUserServicesDir, "snap.*.socket"), 60 filepath.Join(dirs.SnapUserServicesDir, "snap.*.timer"), 61 filepath.Join(dirs.SnapUserServicesDir, "default.target.wants", "snap.*.service"), 62 filepath.Join(dirs.SnapUserServicesDir, "sockets.target.wants", "snap.*.socket"), 63 filepath.Join(dirs.SnapUserServicesDir, "timers.target.wants", "snap.*.timer"), 64 filepath.Join(runinhibit.InhibitDir, "*.lock"), 65 } 66 67 for _, gl := range globs { 68 matches, err := filepath.Glob(filepath.Join(preseedChroot, gl)) 69 if err != nil { 70 // the only possible error from Glob() is ErrBadPattern 71 return err 72 } 73 for _, path := range matches { 74 if err := os.Remove(path); err != nil { 75 return fmt.Errorf("error removing %s: %v", path, err) 76 } 77 } 78 } 79 80 // directories that need to be removed recursively (but 81 // leaving parent directory intact). 82 globs = []string{ 83 filepath.Join(dirs.SnapDataDir, "*"), 84 filepath.Join(dirs.SnapCacheDir, "*"), 85 filepath.Join(apparmor_sandbox.CacheDir, "*"), 86 filepath.Join(dirs.SnapDesktopFilesDir, "*"), 87 filepath.Join(dirs.SnapDBusSessionServicesDir, "*"), 88 filepath.Join(dirs.SnapDBusSystemServicesDir, "*"), 89 } 90 91 for _, gl := range globs { 92 matches, err := filepath.Glob(filepath.Join(preseedChroot, gl)) 93 if err != nil { 94 // the only possible error from Glob() is ErrBadPattern 95 return err 96 } 97 for _, path := range matches { 98 if err := os.RemoveAll(path); err != nil { 99 return fmt.Errorf("error removing %s: %v", path, err) 100 } 101 } 102 } 103 104 // directories removed entirely 105 paths := []string{ 106 dirs.SnapAssertsDBDir, 107 dirs.FeaturesDir, 108 dirs.SnapDesktopIconsDir, 109 dirs.SnapDeviceDir, 110 dirs.SnapCookieDir, 111 dirs.SnapMountPolicyDir, 112 dirs.SnapAppArmorDir, 113 dirs.SnapSeqDir, 114 dirs.SnapMountDir, 115 dirs.SnapSeccompBase, 116 } 117 118 for _, path := range paths { 119 if err := os.RemoveAll(filepath.Join(preseedChroot, path)); err != nil { 120 // report the error and carry on 121 return fmt.Errorf("error removing %s: %v", path, err) 122 } 123 } 124 125 // bash-completion symlinks; note there are symlinks that point at 126 // completer, and symlinks that point at the completer symlinks. 127 // e.g. 128 // lxd.lxc -> /snap/core/current/usr/lib/snapd/complete.sh 129 // lxc -> lxd.lxc 130 files, err := ioutil.ReadDir(filepath.Join(preseedChroot, dirs.CompletersDir)) 131 if err != nil && !os.IsNotExist(err) { 132 return fmt.Errorf("error reading %s: %v", dirs.CompletersDir, err) 133 } 134 completeShSymlinks := make(map[string]string) 135 var otherSymlinks []string 136 137 // pass 1: find all symlinks pointing at complete.sh 138 for _, fileInfo := range files { 139 if fileInfo.Mode()&os.ModeSymlink == 0 { 140 continue 141 } 142 fullPath := filepath.Join(preseedChroot, dirs.CompletersDir, fileInfo.Name()) 143 if dirs.IsCompleteShSymlink(fullPath) { 144 if err := os.Remove(fullPath); err != nil { 145 return fmt.Errorf("error removing symlink %s: %v", fullPath, err) 146 } 147 completeShSymlinks[fileInfo.Name()] = fullPath 148 } else { 149 otherSymlinks = append(otherSymlinks, fullPath) 150 } 151 } 152 // pass 2: find all symlinks that point at the symlinks found in pass 1. 153 for _, other := range otherSymlinks { 154 target, err := os.Readlink(other) 155 if err != nil { 156 return fmt.Errorf("error reading symlink target of %s: %v", other, err) 157 } 158 if _, ok := completeShSymlinks[target]; ok { 159 if err := os.Remove(other); err != nil { 160 return fmt.Errorf("error removing symlink %s: %v", other, err) 161 } 162 } 163 } 164 165 return nil 166 }