github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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/strutil" 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 := makeCookie() 78 snapCookies[cookie] = snap 79 changed = true 80 } 81 } 82 83 content := make(map[string]*osutil.FileState) 84 for cookie, snap := range snapCookies { 85 content[fmt.Sprintf("snap.%s", snap)] = &osutil.FileState{ 86 Content: []byte(cookie), 87 Mode: 0600, 88 } 89 } 90 if _, _, err := osutil.EnsureDirState(dirs.SnapCookieDir, "snap.*", content); err != nil { 91 return fmt.Errorf("Failed to synchronize snap cookies: %s", err) 92 } 93 94 if changed { 95 st.Set("snap-cookies", &snapCookies) 96 } 97 return nil 98 } 99 100 func (m *SnapManager) createSnapCookie(st *state.State, instanceName string) error { 101 snapCookies, err := cookies(st) 102 if err != nil { 103 return err 104 } 105 106 // make sure we don't create cookie if it already exists 107 for _, snap := range snapCookies { 108 if instanceName == snap { 109 return nil 110 } 111 } 112 113 cookieID, err := createCookieFile(instanceName) 114 if err != nil { 115 return err 116 } 117 118 snapCookies[cookieID] = instanceName 119 st.Set("snap-cookies", &snapCookies) 120 return nil 121 } 122 123 func (m *SnapManager) removeSnapCookie(st *state.State, instanceName string) error { 124 if err := removeCookieFile(instanceName); err != nil { 125 return err 126 } 127 128 var snapCookies map[string]string 129 err := st.Get("snap-cookies", &snapCookies) 130 if err != nil { 131 if err != state.ErrNoState { 132 return fmt.Errorf("cannot get snap cookies: %v", err) 133 } 134 // no cookies in the state 135 return nil 136 } 137 138 for cookieID, snap := range snapCookies { 139 if instanceName == snap { 140 delete(snapCookies, cookieID) 141 st.Set("snap-cookies", snapCookies) 142 return nil 143 } 144 } 145 return nil 146 } 147 148 func makeCookie() string { 149 return strutil.MakeRandomString(44) 150 } 151 152 func createCookieFile(instanceName string) (cookieID string, err error) { 153 cookieID = makeCookie() 154 path := filepath.Join(dirs.SnapCookieDir, fmt.Sprintf("snap.%s", instanceName)) 155 err = osutil.AtomicWriteFile(path, []byte(cookieID), 0600, 0) 156 if err != nil { 157 return "", fmt.Errorf("Failed to create cookie file %q: %s", path, err) 158 } 159 return cookieID, nil 160 } 161 162 func removeCookieFile(instanceName string) error { 163 path := filepath.Join(dirs.SnapCookieDir, fmt.Sprintf("snap.%s", instanceName)) 164 if err := os.Remove(path); err != nil && !os.IsNotExist(err) { 165 return fmt.Errorf("Failed to remove cookie file %q: %s", path, err) 166 } 167 return nil 168 }