github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/bootloader/assets/assets.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 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 assets 21 22 import ( 23 "fmt" 24 "sort" 25 26 "github.com/snapcore/snapd/osutil" 27 ) 28 29 var registeredAssets = map[string][]byte{} 30 31 // ForEditions wraps a snippet that is used in editions starting with 32 // FirstEdition. 33 type ForEditions struct { 34 // First edition this snippet is used in 35 FirstEdition uint 36 // Snippet data 37 Snippet []byte 38 } 39 40 var registeredEditionSnippets = map[string][]ForEditions{} 41 42 // registerInternal registers an internal asset under the given name. 43 func registerInternal(name string, data []byte) { 44 if _, ok := registeredAssets[name]; ok { 45 panic(fmt.Sprintf("asset %q is already registered", name)) 46 } 47 registeredAssets[name] = data 48 } 49 50 // Internal returns the content of an internal asset registered under the given 51 // name, or nil when none was found. 52 func Internal(name string) []byte { 53 return registeredAssets[name] 54 } 55 56 type byFirstEdition []ForEditions 57 58 func (b byFirstEdition) Len() int { return len(b) } 59 func (b byFirstEdition) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 60 func (b byFirstEdition) Less(i, j int) bool { return b[i].FirstEdition < b[j].FirstEdition } 61 62 func sanitizeSnippets(snippets []ForEditions) error { 63 if !sort.IsSorted(byFirstEdition(snippets)) { 64 return fmt.Errorf("snippets must be sorted in ascending edition number order") 65 } 66 for i := range snippets { 67 if i == 0 { 68 continue 69 } 70 if snippets[i-1].FirstEdition == snippets[i].FirstEdition { 71 return fmt.Errorf(`first edition %v repeated`, snippets[i].FirstEdition) 72 } 73 } 74 return nil 75 } 76 77 // registerSnippetForEditions register a set of snippets, each carrying the 78 // first edition number it applies to, under a given key. 79 func registerSnippetForEditions(name string, snippets []ForEditions) { 80 if _, ok := registeredEditionSnippets[name]; ok { 81 panic(fmt.Sprintf("edition snippets %q are already registered", name)) 82 } 83 84 if err := sanitizeSnippets(snippets); err != nil { 85 panic(fmt.Errorf("cannot validate snippets %q: %v", name, err)) 86 } 87 registeredEditionSnippets[name] = snippets 88 } 89 90 // SnippetForEdition returns a snippet registered under given name, 91 // applicable for the provided edition number. 92 func SnippetForEdition(name string, edition uint) []byte { 93 snippets := registeredEditionSnippets[name] 94 if snippets == nil { 95 return nil 96 } 97 var current []byte 98 // snippets are sorted by ascending edition number when adding 99 for _, snip := range snippets { 100 if edition >= snip.FirstEdition { 101 current = snip.Snippet 102 } else { 103 break 104 } 105 } 106 return current 107 } 108 109 // MockInternal mocks the contents of an internal asset for use in testing. 110 func MockInternal(name string, data []byte) (restore func()) { 111 osutil.MustBeTestBinary("mocking can be done only in tests") 112 113 old, ok := registeredAssets[name] 114 registeredAssets[name] = data 115 return func() { 116 if ok { 117 registeredAssets[name] = old 118 } else { 119 delete(registeredAssets, name) 120 } 121 } 122 } 123 124 // MockSnippetsForEdition mocks the contents of per-edition snippets. 125 func MockSnippetsForEdition(name string, snippets []ForEditions) (restore func()) { 126 osutil.MustBeTestBinary("mocking can be done only in tests") 127 128 old, ok := registeredEditionSnippets[name] 129 snippetsCopy := make([]ForEditions, len(snippets)) 130 copy(snippetsCopy, snippets) 131 if ok { 132 delete(registeredEditionSnippets, name) 133 } 134 registerSnippetForEditions(name, snippetsCopy) 135 136 return func() { 137 if ok { 138 registeredEditionSnippets[name] = old 139 } else { 140 delete(registeredAssets, name) 141 } 142 } 143 }