github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/bootloader/asset.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 bootloader 21 22 import ( 23 "bufio" 24 "bytes" 25 "errors" 26 "fmt" 27 "io" 28 "os" 29 "strconv" 30 "strings" 31 32 "github.com/snapcore/snapd/bootloader/assets" 33 ) 34 35 var errNoEdition = errors.New("no edition") 36 37 // editionFromDiskConfigAsset extracts the edition information from a boot 38 // config asset on disk. 39 func editionFromDiskConfigAsset(p string) (uint, error) { 40 f, err := os.Open(p) 41 if err != nil { 42 if os.IsNotExist(err) { 43 return 0, errNoEdition 44 } 45 return 0, fmt.Errorf("cannot load existing config asset: %v", err) 46 } 47 defer f.Close() 48 return editionFromConfigAsset(f) 49 } 50 51 const editionHeader = "# Snapd-Boot-Config-Edition: " 52 53 // editionFromConfigAsset extracts edition information from boot config asset. 54 func editionFromConfigAsset(asset io.Reader) (uint, error) { 55 scanner := bufio.NewScanner(asset) 56 if !scanner.Scan() { 57 err := fmt.Errorf("cannot read config asset: unexpected EOF") 58 if sErr := scanner.Err(); sErr != nil { 59 err = fmt.Errorf("cannot read config asset: %v", err) 60 } 61 return 0, err 62 } 63 64 line := scanner.Text() 65 if !strings.HasPrefix(line, editionHeader) { 66 return 0, errNoEdition 67 } 68 69 editionStr := line[len(editionHeader):] 70 editionStr = strings.TrimSpace(editionStr) 71 edition, err := strconv.ParseUint(editionStr, 10, 32) 72 if err != nil { 73 return 0, fmt.Errorf("cannot parse asset edition: %v", err) 74 } 75 return uint(edition), nil 76 } 77 78 // editionFromInternalConfigAsset extracts edition information from a named 79 // internal boot config asset. 80 func editionFromInternalConfigAsset(assetName string) (uint, error) { 81 data := assets.Internal(assetName) 82 if data == nil { 83 return 0, fmt.Errorf("internal error: no boot asset for %q", assetName) 84 } 85 return editionFromConfigAsset(bytes.NewReader(data)) 86 } 87 88 // configAsset is a boot config asset, such as text script, used by grub or 89 // u-boot. 90 type configAsset struct { 91 body []byte 92 parsedEdition uint 93 } 94 95 func (g *configAsset) Edition() uint { 96 return g.parsedEdition 97 } 98 99 func (g *configAsset) Raw() []byte { 100 return g.body 101 } 102 103 func configAssetFrom(data []byte) (*configAsset, error) { 104 edition, err := editionFromConfigAsset(bytes.NewReader(data)) 105 if err != nil && err != errNoEdition { 106 return nil, err 107 } 108 gbs := &configAsset{ 109 body: data, 110 parsedEdition: edition, 111 } 112 return gbs, nil 113 }