github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/snapstate/backend/aliases.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2017 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 "strings" 27 "syscall" 28 29 "github.com/snapcore/snapd/dirs" 30 ) 31 32 // Alias represents a command alias with a name and its application target. 33 type Alias struct { 34 Name string `json:"name"` 35 Target string `json:"target"` 36 } 37 38 // MatchingAliases returns the subset of aliases that exist on disk and have the expected targets. 39 40 // UpdateAliases adds and removes the given aliases. 41 func (b Backend) UpdateAliases(add []*Alias, remove []*Alias) error { 42 removed := make(map[string]bool, len(remove)) 43 for _, alias := range remove { 44 err := os.Remove(filepath.Join(dirs.SnapBinariesDir, alias.Name)) 45 if err != nil && !os.IsNotExist(err) { 46 return fmt.Errorf("cannot remove alias symlink: %v", err) 47 } 48 removed[alias.Name] = true 49 completer := filepath.Join(dirs.CompletersDir, alias.Name) 50 target, err := os.Readlink(completer) 51 if err != nil || target != alias.Target { 52 continue 53 } 54 os.Remove(completer) 55 } 56 57 for _, alias := range add { 58 p := filepath.Join(dirs.SnapBinariesDir, alias.Name) 59 60 if !removed[alias.Name] { 61 if err := os.Remove(p); err != nil && !os.IsNotExist(err) { 62 return fmt.Errorf("cannot remove alias symlink: %v", err) 63 } 64 } 65 66 err := os.Symlink(alias.Target, p) 67 if err != nil { 68 return fmt.Errorf("cannot create alias symlink: %v", err) 69 } 70 71 if dirs.IsCompleteShSymlink(filepath.Join(dirs.CompletersDir, alias.Target)) { 72 os.Symlink(alias.Target, filepath.Join(dirs.CompletersDir, alias.Name)) 73 } 74 } 75 return nil 76 } 77 78 // RemoveSnapAliases removes all the aliases targeting the given snap. 79 func (b Backend) RemoveSnapAliases(snapName string) error { 80 removeSymlinksTo(dirs.CompletersDir, snapName) 81 return removeSymlinksTo(dirs.SnapBinariesDir, snapName) 82 } 83 84 func removeSymlinksTo(dir, snapName string) error { 85 cands, err := filepath.Glob(filepath.Join(dir, "*")) 86 if err != nil { 87 return err 88 } 89 prefix := fmt.Sprintf("%s.", snapName) 90 var firstErr error 91 // best effort 92 for _, cand := range cands { 93 target, err := os.Readlink(cand) 94 if err, ok := err.(*os.PathError); ok && err.Err == syscall.EINVAL { 95 continue 96 } 97 if err != nil { 98 if firstErr == nil { 99 firstErr = err 100 } 101 continue 102 } 103 if target == snapName || strings.HasPrefix(target, prefix) { 104 err := os.Remove(cand) 105 if err != nil && firstErr == nil { 106 firstErr = fmt.Errorf("cannot remove alias symlink: %v", err) 107 } 108 } 109 } 110 return firstErr 111 }