github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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 // registerSnippetForEditions register a set of snippets, each carrying the 63 // first edition number it applies to, under a given key. 64 func registerSnippetForEditions(name string, snippets []ForEditions) { 65 if _, ok := registeredEditionSnippets[name]; ok { 66 panic(fmt.Sprintf("edition snippets %q are already registered", name)) 67 } 68 69 if !sort.IsSorted(byFirstEdition(snippets)) { 70 panic(fmt.Sprintf("edition snippets %q must be sorted in ascending edition number order", name)) 71 } 72 for i := range snippets { 73 if i == 0 { 74 continue 75 } 76 if snippets[i-1].FirstEdition == snippets[i].FirstEdition { 77 panic(fmt.Sprintf(`first edition %v repeated in edition snippets %q`, 78 snippets[i].FirstEdition, name)) 79 } 80 } 81 registeredEditionSnippets[name] = snippets 82 } 83 84 // SnippetForEdition returns a snippet registered under given name, 85 // applicable for the provided edition number. 86 func SnippetForEdition(name string, edition uint) []byte { 87 snippets := registeredEditionSnippets[name] 88 if snippets == nil { 89 return nil 90 } 91 var current []byte 92 // snippets are sorted by ascending edition number when adding 93 for _, snip := range snippets { 94 if edition >= snip.FirstEdition { 95 current = snip.Snippet 96 } else { 97 break 98 } 99 } 100 return current 101 } 102 103 // MockInternal mocks the contents of an internal asset for use in testing. 104 func MockInternal(name string, data []byte) (restore func()) { 105 osutil.MustBeTestBinary("mocking can be done only in tests") 106 107 old, ok := registeredAssets[name] 108 registeredAssets[name] = data 109 return func() { 110 if ok { 111 registeredAssets[name] = old 112 } else { 113 delete(registeredAssets, name) 114 } 115 } 116 } 117 118 // MockSnippetsForEdition mocks the contents of per-edition snippets. 119 func MockSnippetsForEdition(name string, snippets []ForEditions) (restore func()) { 120 osutil.MustBeTestBinary("mocking can be done only in tests") 121 122 old, ok := registeredEditionSnippets[name] 123 snippetsCopy := make([]ForEditions, len(snippets)) 124 copy(snippetsCopy, snippets) 125 if ok { 126 delete(registeredEditionSnippets, name) 127 } 128 registerSnippetForEditions(name, snippetsCopy) 129 130 return func() { 131 if ok { 132 registeredEditionSnippets[name] = old 133 } else { 134 delete(registeredAssets, name) 135 } 136 } 137 }