gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/snapstate/cookies.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 snapstate 21 22 import ( 23 "encoding/json" 24 "fmt" 25 "os" 26 "path/filepath" 27 28 "github.com/snapcore/snapd/dirs" 29 "github.com/snapcore/snapd/osutil" 30 "github.com/snapcore/snapd/overlord/state" 31 "github.com/snapcore/snapd/randutil" 32 ) 33 34 func cookies(st *state.State) (map[string]string, error) { 35 var snapCookies map[string]string 36 if err := st.Get("snap-cookies", &snapCookies); err != nil { 37 if err != state.ErrNoState { 38 return nil, fmt.Errorf("cannot get snap cookies: %v", err) 39 } 40 snapCookies = make(map[string]string) 41 } 42 return snapCookies, nil 43 } 44 45 // SyncCookies creates snap cookies for snaps that are missing them (may be the case for snaps installed 46 // before the feature of running snapctl outside of hooks was introduced, leading to a warning 47 // from snap-confine). 48 // It is the caller's responsibility to lock state before calling this function. 49 func (m *SnapManager) SyncCookies(st *state.State) error { 50 var instanceNames map[string]*json.RawMessage 51 if err := st.Get("snaps", &instanceNames); err != nil && err != state.ErrNoState { 52 return err 53 } 54 55 snapCookies, err := cookies(st) 56 if err != nil { 57 return err 58 } 59 60 snapsWithCookies := make(map[string]bool) 61 for _, snap := range snapCookies { 62 // check if we have a cookie for non-installed snap or if we have a duplicated cookie 63 if _, ok := instanceNames[snap]; !ok || snapsWithCookies[snap] { 64 // there is no point in checking all cookies if we found a bad one - recreate them all 65 snapCookies = make(map[string]string) 66 snapsWithCookies = make(map[string]bool) 67 break 68 } 69 snapsWithCookies[snap] = true 70 } 71 72 var changed bool 73 74 // make sure every snap has a cookie, generate one if necessary 75 for snap := range instanceNames { 76 if _, ok := snapsWithCookies[snap]; !ok { 77 cookie, err := makeCookie() 78 if err != nil { 79 return err 80 } 81 snapCookies[cookie] = snap 82 changed = true 83 } 84 } 85 86 content := make(map[string]osutil.FileState) 87 for cookie, snap := range snapCookies { 88 content[fmt.Sprintf("snap.%s", snap)] = &osutil.MemoryFileState{ 89 Content: []byte(cookie), 90 Mode: 0600, 91 } 92 } 93 if _, _, err := osutil.EnsureDirState(dirs.SnapCookieDir, "snap.*", content); err != nil { 94 return fmt.Errorf("Failed to synchronize snap cookies: %s", err) 95 } 96 97 if changed { 98 st.Set("snap-cookies", &snapCookies) 99 } 100 return nil 101 } 102 103 func (m *SnapManager) createSnapCookie(st *state.State, instanceName string) error { 104 snapCookies, err := cookies(st) 105 if err != nil { 106 return err 107 } 108 109 // make sure we don't create cookie if it already exists 110 for _, snap := range snapCookies { 111 if instanceName == snap { 112 return nil 113 } 114 } 115 116 cookieID, err := createCookieFile(instanceName) 117 if err != nil { 118 return err 119 } 120 121 snapCookies[cookieID] = instanceName 122 st.Set("snap-cookies", &snapCookies) 123 return nil 124 } 125 126 func (m *SnapManager) removeSnapCookie(st *state.State, instanceName string) error { 127 if err := removeCookieFile(instanceName); err != nil { 128 return err 129 } 130 131 var snapCookies map[string]string 132 err := st.Get("snap-cookies", &snapCookies) 133 if err != nil { 134 if err != state.ErrNoState { 135 return fmt.Errorf("cannot get snap cookies: %v", err) 136 } 137 // no cookies in the state 138 return nil 139 } 140 141 for cookieID, snap := range snapCookies { 142 if instanceName == snap { 143 delete(snapCookies, cookieID) 144 st.Set("snap-cookies", snapCookies) 145 return nil 146 } 147 } 148 return nil 149 } 150 151 func makeCookie() (string, error) { 152 return randutil.CryptoToken(39) 153 } 154 155 func createCookieFile(instanceName string) (cookieID string, err error) { 156 cookieID, err = makeCookie() 157 if err != nil { 158 return "", err 159 } 160 path := filepath.Join(dirs.SnapCookieDir, fmt.Sprintf("snap.%s", instanceName)) 161 err = osutil.AtomicWriteFile(path, []byte(cookieID), 0600, 0) 162 if err != nil { 163 return "", fmt.Errorf("Failed to create cookie file %q: %s", path, err) 164 } 165 return cookieID, nil 166 } 167 168 func removeCookieFile(instanceName string) error { 169 path := filepath.Join(dirs.SnapCookieDir, fmt.Sprintf("snap.%s", instanceName)) 170 if err := os.Remove(path); err != nil && !os.IsNotExist(err) { 171 return fmt.Errorf("Failed to remove cookie file %q: %s", path, err) 172 } 173 return nil 174 }