gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/fsbackstore.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2022 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 asserts 21 22 import ( 23 "errors" 24 "fmt" 25 "net/url" 26 "os" 27 "path/filepath" 28 "strconv" 29 "strings" 30 "sync" 31 ) 32 33 // the default filesystem based backstore for assertions 34 35 const ( 36 assertionsLayoutVersion = "v0" 37 assertionsRoot = "asserts-" + assertionsLayoutVersion 38 ) 39 40 type filesystemBackstore struct { 41 top string 42 mu sync.RWMutex 43 } 44 45 // OpenFSBackstore opens a filesystem backed assertions backstore under path. 46 func OpenFSBackstore(path string) (Backstore, error) { 47 top := filepath.Join(path, assertionsRoot) 48 err := ensureTop(top) 49 if err != nil { 50 return nil, err 51 } 52 return &filesystemBackstore{top: top}, nil 53 } 54 55 // guarantees that result assertion is of the expected type (both in the AssertionType and go type sense) 56 func (fsbs *filesystemBackstore) readAssertion(assertType *AssertionType, diskPrimaryPath string) (Assertion, error) { 57 encoded, err := readEntry(fsbs.top, assertType.Name, diskPrimaryPath) 58 if os.IsNotExist(err) { 59 return nil, errNotFound 60 } 61 if err != nil { 62 return nil, fmt.Errorf("broken assertion storage, cannot read assertion: %v", err) 63 } 64 assert, err := Decode(encoded) 65 if err != nil { 66 return nil, fmt.Errorf("broken assertion storage, cannot decode assertion: %v", err) 67 } 68 if assert.Type() != assertType { 69 return nil, fmt.Errorf("assertion that is not of type %q under their storage tree", assertType.Name) 70 } 71 // because of Decode() construction assert has also the expected go type 72 return assert, nil 73 } 74 75 func (fsbs *filesystemBackstore) pickLatestAssertion(assertType *AssertionType, diskPrimaryPaths []string, maxFormat int) (a Assertion, er error) { 76 for _, diskPrimaryPath := range diskPrimaryPaths { 77 fn := filepath.Base(diskPrimaryPath) 78 parts := strings.SplitN(fn, ".", 2) 79 formatnum := 0 80 if len(parts) == 2 { 81 var err error 82 formatnum, err = strconv.Atoi(parts[1]) 83 if err != nil { 84 return nil, fmt.Errorf("invalid active assertion filename: %q", fn) 85 } 86 } 87 if formatnum <= maxFormat { 88 a1, err := fsbs.readAssertion(assertType, diskPrimaryPath) 89 if err != nil { 90 return nil, err 91 } 92 if a == nil || a1.Revision() > a.Revision() { 93 a = a1 94 } 95 } 96 } 97 if a == nil { 98 return nil, errNotFound 99 } 100 return a, nil 101 } 102 103 // diskPrimaryPathComps computes the components of the path for an assertion. 104 // The path will look like this: (all <comp> are query escaped) 105 // <primaryPath0>/<primaryPath1>...[/0:<optPrimaryPath0>[/1:<optPrimaryPath1>]...]/<active> 106 // The components #:<value> for the optional primary path values 107 // appear only if their value is not the default. 108 // This makes it so that assertions with default values have the same 109 // paths as for snapd versions without those optional primary keys 110 // yet. 111 func diskPrimaryPathComps(assertType *AssertionType, primaryPath []string, active string) []string { 112 n := len(primaryPath) 113 comps := make([]string, 0, n+1) 114 // safety against '/' etc 115 noptional := -1 116 for i, comp := range primaryPath { 117 defl := assertType.OptionalPrimaryKeyDefaults[assertType.PrimaryKey[i]] 118 qvalue := url.QueryEscape(comp) 119 if defl != "" { 120 noptional++ 121 if comp == defl { 122 continue 123 } 124 qvalue = fmt.Sprintf("%d:%s", noptional, qvalue) 125 } 126 comps = append(comps, qvalue) 127 } 128 comps = append(comps, active) 129 return comps 130 } 131 132 func (fsbs *filesystemBackstore) currentAssertion(assertType *AssertionType, primaryPath []string, maxFormat int) (Assertion, error) { 133 var a Assertion 134 namesCb := func(relpaths []string) error { 135 var err error 136 a, err = fsbs.pickLatestAssertion(assertType, relpaths, maxFormat) 137 if err == errNotFound { 138 return nil 139 } 140 return err 141 } 142 143 comps := diskPrimaryPathComps(assertType, primaryPath, "active*") 144 assertTypeTop := filepath.Join(fsbs.top, assertType.Name) 145 err := findWildcard(assertTypeTop, comps, 0, namesCb) 146 if err != nil { 147 return nil, fmt.Errorf("broken assertion storage, looking for %s: %v", assertType.Name, err) 148 } 149 150 if a == nil { 151 return nil, errNotFound 152 } 153 154 return a, nil 155 } 156 157 func (fsbs *filesystemBackstore) Put(assertType *AssertionType, assert Assertion) error { 158 fsbs.mu.Lock() 159 defer fsbs.mu.Unlock() 160 161 primaryPath := assert.Ref().PrimaryKey 162 163 curAssert, err := fsbs.currentAssertion(assertType, primaryPath, assertType.MaxSupportedFormat()) 164 if err == nil { 165 curRev := curAssert.Revision() 166 rev := assert.Revision() 167 if curRev >= rev { 168 return &RevisionError{Current: curRev, Used: rev} 169 } 170 } else if err != errNotFound { 171 return err 172 } 173 174 formatnum := assert.Format() 175 activeFn := "active" 176 if formatnum > 0 { 177 activeFn = fmt.Sprintf("active.%d", formatnum) 178 } 179 diskPrimaryPath := filepath.Join(diskPrimaryPathComps(assertType, primaryPath, activeFn)...) 180 err = atomicWriteEntry(Encode(assert), false, fsbs.top, assertType.Name, diskPrimaryPath) 181 if err != nil { 182 return fmt.Errorf("broken assertion storage, cannot write assertion: %v", err) 183 } 184 return nil 185 } 186 187 func (fsbs *filesystemBackstore) Get(assertType *AssertionType, key []string, maxFormat int) (Assertion, error) { 188 fsbs.mu.RLock() 189 defer fsbs.mu.RUnlock() 190 191 if len(key) > len(assertType.PrimaryKey) { 192 return nil, fmt.Errorf("internal error: Backstore.Get given a key longer than expected for %q: %v", assertType.Name, key) 193 } 194 195 a, err := fsbs.currentAssertion(assertType, key, maxFormat) 196 if err == errNotFound { 197 return nil, &NotFoundError{Type: assertType} 198 } 199 return a, err 200 } 201 202 func (fsbs *filesystemBackstore) search(assertType *AssertionType, diskPattern []string, foundCb func(Assertion), maxFormat int) error { 203 assertTypeTop := filepath.Join(fsbs.top, assertType.Name) 204 candCb := func(diskPrimaryPaths []string) error { 205 a, err := fsbs.pickLatestAssertion(assertType, diskPrimaryPaths, maxFormat) 206 if err == errNotFound { 207 return nil 208 } 209 if err != nil { 210 return err 211 } 212 foundCb(a) 213 return nil 214 } 215 err := findWildcard(assertTypeTop, diskPattern, 0, candCb) 216 if err != nil { 217 return fmt.Errorf("broken assertion storage, searching for %s: %v", assertType.Name, err) 218 } 219 return nil 220 } 221 222 func (fsbs *filesystemBackstore) searchOptional(assertType *AssertionType, kopt, pattPos, firstOpt int, diskPattern []string, headers map[string]string, foundCb func(Assertion), maxFormat int) error { 223 if kopt == len(assertType.PrimaryKey) { 224 candCb := func(a Assertion) { 225 if searchMatch(a, headers) { 226 foundCb(a) 227 } 228 } 229 230 diskPattern[pattPos] = "active*" 231 return fsbs.search(assertType, diskPattern[:pattPos+1], candCb, maxFormat) 232 } 233 k := assertType.PrimaryKey[kopt] 234 keyVal := headers[k] 235 switch keyVal { 236 case "": 237 diskPattern[pattPos] = fmt.Sprintf("%d:*", kopt-firstOpt) 238 if err := fsbs.searchOptional(assertType, kopt+1, pattPos+1, firstOpt, diskPattern, headers, foundCb, maxFormat); err != nil { 239 return err 240 } 241 fallthrough 242 case assertType.OptionalPrimaryKeyDefaults[k]: 243 return fsbs.searchOptional(assertType, kopt+1, pattPos, firstOpt, diskPattern, headers, foundCb, maxFormat) 244 default: 245 diskPattern[pattPos] = fmt.Sprintf("%d:%s", kopt-firstOpt, url.QueryEscape(keyVal)) 246 return fsbs.searchOptional(assertType, kopt+1, pattPos+1, firstOpt, diskPattern, headers, foundCb, maxFormat) 247 } 248 } 249 250 func (fsbs *filesystemBackstore) Search(assertType *AssertionType, headers map[string]string, foundCb func(Assertion), maxFormat int) error { 251 fsbs.mu.RLock() 252 defer fsbs.mu.RUnlock() 253 254 n := len(assertType.PrimaryKey) 255 nopt := len(assertType.OptionalPrimaryKeyDefaults) 256 diskPattern := make([]string, n+1) 257 for i, k := range assertType.PrimaryKey[:n-nopt] { 258 keyVal := headers[k] 259 if keyVal == "" { 260 diskPattern[i] = "*" 261 } else { 262 diskPattern[i] = url.QueryEscape(keyVal) 263 } 264 } 265 pattPos := n - nopt 266 267 return fsbs.searchOptional(assertType, pattPos, pattPos, pattPos, diskPattern, headers, foundCb, maxFormat) 268 } 269 270 // errFound marks the case an assertion was found 271 var errFound = errors.New("found") 272 273 func (fsbs *filesystemBackstore) SequenceMemberAfter(assertType *AssertionType, sequenceKey []string, after, maxFormat int) (SequenceMember, error) { 274 if !assertType.SequenceForming() { 275 panic(fmt.Sprintf("internal error: SequenceMemberAfter on non sequence-forming assertion type %s", assertType.Name)) 276 } 277 if len(sequenceKey) != len(assertType.PrimaryKey)-1 { 278 return nil, fmt.Errorf("internal error: SequenceMemberAfter's sequence key argument length must be exactly 1 less than the assertion type primary key") 279 } 280 281 fsbs.mu.RLock() 282 defer fsbs.mu.RUnlock() 283 284 n := len(assertType.PrimaryKey) 285 diskPattern := make([]string, n+1) 286 for i, k := range sequenceKey { 287 diskPattern[i] = url.QueryEscape(k) 288 } 289 seqWildcard := "#>" // ascending sequence wildcard 290 if after == -1 { 291 // find the latest in sequence 292 // use descending sequence wildcard 293 seqWildcard = "#<" 294 } 295 diskPattern[n-1] = seqWildcard 296 diskPattern[n] = "active*" 297 298 var a Assertion 299 candCb := func(diskPrimaryPaths []string) error { 300 var err error 301 a, err = fsbs.pickLatestAssertion(assertType, diskPrimaryPaths, maxFormat) 302 if err == errNotFound { 303 return nil 304 } 305 if err != nil { 306 return err 307 } 308 return errFound 309 } 310 311 assertTypeTop := filepath.Join(fsbs.top, assertType.Name) 312 err := findWildcard(assertTypeTop, diskPattern, after, candCb) 313 if err == errFound { 314 return a.(SequenceMember), nil 315 } 316 if err != nil { 317 return nil, fmt.Errorf("broken assertion storage, searching for %s: %v", assertType.Name, err) 318 } 319 320 return nil, &NotFoundError{Type: assertType} 321 }