Ai
1 Star 0 Fork 1

mysnapcore/mysnapd

forked from tupelo-shen/mysnapd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
fsbackstore.go 9.78 KB
一键复制 编辑 原始数据 按行查看 历史
tupelo-shen 提交于 2022-11-07 17:55 +08:00 . fix: aspects & asserts commit
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2015-2022 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package asserts
import (
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
)
// the default filesystem based backstore for assertions
const (
assertionsLayoutVersion = "v0"
assertionsRoot = "asserts-" + assertionsLayoutVersion
)
type filesystemBackstore struct {
top string
mu sync.RWMutex
}
// OpenFSBackstore opens a filesystem backed assertions backstore under path.
func OpenFSBackstore(path string) (Backstore, error) {
top := filepath.Join(path, assertionsRoot)
err := ensureTop(top)
if err != nil {
return nil, err
}
return &filesystemBackstore{top: top}, nil
}
// guarantees that result assertion is of the expected type (both in the AssertionType and go type sense)
func (fsbs *filesystemBackstore) readAssertion(assertType *AssertionType, diskPrimaryPath string) (Assertion, error) {
encoded, err := readEntry(fsbs.top, assertType.Name, diskPrimaryPath)
if os.IsNotExist(err) {
return nil, errNotFound
}
if err != nil {
return nil, fmt.Errorf("broken assertion storage, cannot read assertion: %v", err)
}
assert, err := Decode(encoded)
if err != nil {
return nil, fmt.Errorf("broken assertion storage, cannot decode assertion: %v", err)
}
if assert.Type() != assertType {
return nil, fmt.Errorf("assertion that is not of type %q under their storage tree", assertType.Name)
}
// because of Decode() construction assert has also the expected go type
return assert, nil
}
func (fsbs *filesystemBackstore) pickLatestAssertion(assertType *AssertionType, diskPrimaryPaths []string, maxFormat int) (a Assertion, er error) {
for _, diskPrimaryPath := range diskPrimaryPaths {
fn := filepath.Base(diskPrimaryPath)
parts := strings.SplitN(fn, ".", 2)
formatnum := 0
if len(parts) == 2 {
var err error
formatnum, err = strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("invalid active assertion filename: %q", fn)
}
}
if formatnum <= maxFormat {
a1, err := fsbs.readAssertion(assertType, diskPrimaryPath)
if err != nil {
return nil, err
}
if a == nil || a1.Revision() > a.Revision() {
a = a1
}
}
}
if a == nil {
return nil, errNotFound
}
return a, nil
}
// diskPrimaryPathComps computes the components of the path for an assertion.
// The path will look like this: (all <comp> are query escaped)
// <primaryPath0>/<primaryPath1>...[/0:<optPrimaryPath0>[/1:<optPrimaryPath1>]...]/<active>
// The components #:<value> for the optional primary path values
// appear only if their value is not the default.
// This makes it so that assertions with default values have the same
// paths as for snapd versions without those optional primary keys
// yet.
func diskPrimaryPathComps(assertType *AssertionType, primaryPath []string, active string) []string {
n := len(primaryPath)
comps := make([]string, 0, n+1)
// safety against '/' etc
noptional := -1
for i, comp := range primaryPath {
defl := assertType.OptionalPrimaryKeyDefaults[assertType.PrimaryKey[i]]
qvalue := url.QueryEscape(comp)
if defl != "" {
noptional++
if comp == defl {
continue
}
qvalue = fmt.Sprintf("%d:%s", noptional, qvalue)
}
comps = append(comps, qvalue)
}
comps = append(comps, active)
return comps
}
func (fsbs *filesystemBackstore) currentAssertion(assertType *AssertionType, primaryPath []string, maxFormat int) (Assertion, error) {
var a Assertion
namesCb := func(relpaths []string) error {
var err error
a, err = fsbs.pickLatestAssertion(assertType, relpaths, maxFormat)
if err == errNotFound {
return nil
}
return err
}
comps := diskPrimaryPathComps(assertType, primaryPath, "active*")
assertTypeTop := filepath.Join(fsbs.top, assertType.Name)
err := findWildcard(assertTypeTop, comps, 0, namesCb)
if err != nil {
return nil, fmt.Errorf("broken assertion storage, looking for %s: %v", assertType.Name, err)
}
if a == nil {
return nil, errNotFound
}
return a, nil
}
func (fsbs *filesystemBackstore) Put(assertType *AssertionType, assert Assertion) error {
fsbs.mu.Lock()
defer fsbs.mu.Unlock()
primaryPath := assert.Ref().PrimaryKey
curAssert, err := fsbs.currentAssertion(assertType, primaryPath, assertType.MaxSupportedFormat())
if err == nil {
curRev := curAssert.Revision()
rev := assert.Revision()
if curRev >= rev {
return &RevisionError{Current: curRev, Used: rev}
}
} else if err != errNotFound {
return err
}
formatnum := assert.Format()
activeFn := "active"
if formatnum > 0 {
activeFn = fmt.Sprintf("active.%d", formatnum)
}
diskPrimaryPath := filepath.Join(diskPrimaryPathComps(assertType, primaryPath, activeFn)...)
err = atomicWriteEntry(Encode(assert), false, fsbs.top, assertType.Name, diskPrimaryPath)
if err != nil {
return fmt.Errorf("broken assertion storage, cannot write assertion: %v", err)
}
return nil
}
func (fsbs *filesystemBackstore) Get(assertType *AssertionType, key []string, maxFormat int) (Assertion, error) {
fsbs.mu.RLock()
defer fsbs.mu.RUnlock()
if len(key) > len(assertType.PrimaryKey) {
return nil, fmt.Errorf("internal error: Backstore.Get given a key longer than expected for %q: %v", assertType.Name, key)
}
a, err := fsbs.currentAssertion(assertType, key, maxFormat)
if err == errNotFound {
return nil, &NotFoundError{Type: assertType}
}
return a, err
}
func (fsbs *filesystemBackstore) search(assertType *AssertionType, diskPattern []string, foundCb func(Assertion), maxFormat int) error {
assertTypeTop := filepath.Join(fsbs.top, assertType.Name)
candCb := func(diskPrimaryPaths []string) error {
a, err := fsbs.pickLatestAssertion(assertType, diskPrimaryPaths, maxFormat)
if err == errNotFound {
return nil
}
if err != nil {
return err
}
foundCb(a)
return nil
}
err := findWildcard(assertTypeTop, diskPattern, 0, candCb)
if err != nil {
return fmt.Errorf("broken assertion storage, searching for %s: %v", assertType.Name, err)
}
return nil
}
func (fsbs *filesystemBackstore) searchOptional(assertType *AssertionType, kopt, pattPos, firstOpt int, diskPattern []string, headers map[string]string, foundCb func(Assertion), maxFormat int) error {
if kopt == len(assertType.PrimaryKey) {
candCb := func(a Assertion) {
if searchMatch(a, headers) {
foundCb(a)
}
}
diskPattern[pattPos] = "active*"
return fsbs.search(assertType, diskPattern[:pattPos+1], candCb, maxFormat)
}
k := assertType.PrimaryKey[kopt]
keyVal := headers[k]
switch keyVal {
case "":
diskPattern[pattPos] = fmt.Sprintf("%d:*", kopt-firstOpt)
if err := fsbs.searchOptional(assertType, kopt+1, pattPos+1, firstOpt, diskPattern, headers, foundCb, maxFormat); err != nil {
return err
}
fallthrough
case assertType.OptionalPrimaryKeyDefaults[k]:
return fsbs.searchOptional(assertType, kopt+1, pattPos, firstOpt, diskPattern, headers, foundCb, maxFormat)
default:
diskPattern[pattPos] = fmt.Sprintf("%d:%s", kopt-firstOpt, url.QueryEscape(keyVal))
return fsbs.searchOptional(assertType, kopt+1, pattPos+1, firstOpt, diskPattern, headers, foundCb, maxFormat)
}
}
func (fsbs *filesystemBackstore) Search(assertType *AssertionType, headers map[string]string, foundCb func(Assertion), maxFormat int) error {
fsbs.mu.RLock()
defer fsbs.mu.RUnlock()
n := len(assertType.PrimaryKey)
nopt := len(assertType.OptionalPrimaryKeyDefaults)
diskPattern := make([]string, n+1)
for i, k := range assertType.PrimaryKey[:n-nopt] {
keyVal := headers[k]
if keyVal == "" {
diskPattern[i] = "*"
} else {
diskPattern[i] = url.QueryEscape(keyVal)
}
}
pattPos := n - nopt
return fsbs.searchOptional(assertType, pattPos, pattPos, pattPos, diskPattern, headers, foundCb, maxFormat)
}
// errFound marks the case an assertion was found
var errFound = errors.New("found")
func (fsbs *filesystemBackstore) SequenceMemberAfter(assertType *AssertionType, sequenceKey []string, after, maxFormat int) (SequenceMember, error) {
if !assertType.SequenceForming() {
panic(fmt.Sprintf("internal error: SequenceMemberAfter on non sequence-forming assertion type %s", assertType.Name))
}
if len(sequenceKey) != len(assertType.PrimaryKey)-1 {
return nil, fmt.Errorf("internal error: SequenceMemberAfter's sequence key argument length must be exactly 1 less than the assertion type primary key")
}
fsbs.mu.RLock()
defer fsbs.mu.RUnlock()
n := len(assertType.PrimaryKey)
diskPattern := make([]string, n+1)
for i, k := range sequenceKey {
diskPattern[i] = url.QueryEscape(k)
}
seqWildcard := "#>" // ascending sequence wildcard
if after == -1 {
// find the latest in sequence
// use descending sequence wildcard
seqWildcard = "#<"
}
diskPattern[n-1] = seqWildcard
diskPattern[n] = "active*"
var a Assertion
candCb := func(diskPrimaryPaths []string) error {
var err error
a, err = fsbs.pickLatestAssertion(assertType, diskPrimaryPaths, maxFormat)
if err == errNotFound {
return nil
}
if err != nil {
return err
}
return errFound
}
assertTypeTop := filepath.Join(fsbs.top, assertType.Name)
err := findWildcard(assertTypeTop, diskPattern, after, candCb)
if err == errFound {
return a.(SequenceMember), nil
}
if err != nil {
return nil, fmt.Errorf("broken assertion storage, searching for %s: %v", assertType.Name, err)
}
return nil, &NotFoundError{Type: assertType}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

搜索帮助