github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/uefivars/vars.go (about)

     1  // Copyright 2015-2020 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  //
     5  // SPDX-License-Identifier: BSD-3-Clause
     6  //
     7  
     8  package uefivars
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"log"
    14  	"os"
    15  	fp "path/filepath"
    16  	"strings"
    17  	"unicode/utf16"
    18  	"unicode/utf8"
    19  )
    20  
    21  // http://kurtqiao.github.io/uefi/2015/01/13/uefi-boot-manager.html
    22  
    23  // EfiVarDir is the sysfs /sys/firmware/efi/vars directory, which can be overridden for testing.
    24  var EfiVarDir = "/sys/firmware/efi/vars"
    25  
    26  // EfiVar is a generic efi var.
    27  type EfiVar struct {
    28  	UUID, Name string
    29  	Data       []byte
    30  }
    31  type EfiVars []EfiVar
    32  
    33  func ReadVar(uuid, name string) (e EfiVar, err error) {
    34  	path := fp.Join(EfiVarDir, name+"-"+uuid, "data")
    35  	e.UUID = uuid
    36  	e.Name = name
    37  	e.Data, err = os.ReadFile(path)
    38  	return
    39  }
    40  
    41  // AllVars returns all efi variables
    42  func AllVars() (vars EfiVars) { return ReadVars(nil) }
    43  
    44  // ReadVars returns efi variables matching filter
    45  func ReadVars(filt VarFilter) (vars EfiVars) {
    46  	entries, err := fp.Glob(fp.Join(EfiVarDir, "*-*"))
    47  	if err != nil {
    48  		log.Printf("error reading efi vars: %s", err)
    49  		return
    50  	}
    51  	for _, entry := range entries {
    52  		base := fp.Base(entry)
    53  		n := strings.Count(base, "-")
    54  		if n < 5 {
    55  			log.Printf("skipping %s - not a valid var?", base)
    56  			continue
    57  		}
    58  		components := strings.SplitN(base, "-", 2)
    59  		if filt != nil && !filt(components[1], components[0]) {
    60  			continue
    61  		}
    62  		info, err := os.Stat(entry)
    63  		if err == nil && info.IsDir() {
    64  			v, err := ReadVar(components[1], components[0])
    65  			if err != nil {
    66  				log.Printf("reading efi var %s: %s", base, err)
    67  				continue
    68  			}
    69  			vars = append(vars, v)
    70  		}
    71  	}
    72  	return
    73  }
    74  
    75  // VarFilter is a type of function used to filter efi vars
    76  type VarFilter func(uuid, name string) bool
    77  
    78  // NotFilter returns a filter negating the given filter.
    79  func NotFilter(f VarFilter) VarFilter {
    80  	return func(u, n string) bool { return !f(u, n) }
    81  }
    82  
    83  // AndFilter returns true only if all given filters return true.
    84  func AndFilter(filters ...VarFilter) VarFilter {
    85  	return func(u, n string) bool {
    86  		for _, f := range filters {
    87  			if !f(u, n) {
    88  				return false
    89  			}
    90  		}
    91  		return true
    92  	}
    93  }
    94  
    95  // Filter returns the elements of the list for which the filter function
    96  // returns true.
    97  func (vars EfiVars) Filter(filt VarFilter) EfiVars {
    98  	var res EfiVars
    99  	for _, v := range vars {
   100  		if filt(v.UUID, v.Name) {
   101  			res = append(res, v)
   102  		}
   103  	}
   104  	return res
   105  }
   106  
   107  // DecodeUTF16 decodes the input as a utf16 string.
   108  // https://gist.github.com/bradleypeabody/185b1d7ed6c0c2ab6cec
   109  func DecodeUTF16(b []byte) (string, error) {
   110  	if len(b)%2 != 0 {
   111  		return "", fmt.Errorf("Must have even length byte slice")
   112  	}
   113  
   114  	u16s := make([]uint16, 1)
   115  	ret := &bytes.Buffer{}
   116  	b8buf := make([]byte, 4)
   117  
   118  	lb := len(b)
   119  	for i := 0; i < lb; i += 2 {
   120  		u16s[0] = BytesToU16(b[i : i+2])
   121  		r := utf16.Decode(u16s)
   122  		n := utf8.EncodeRune(b8buf, r[0])
   123  		ret.Write(b8buf[:n])
   124  	}
   125  
   126  	return ret.String(), nil
   127  }
   128  
   129  // BytesToU16 converts a []byte of length 2 to a uint16.
   130  func BytesToU16(b []byte) uint16 {
   131  	if len(b) != 2 {
   132  		log.Fatalf("bytesToU16: bad len %d (%x)", len(b), b)
   133  	}
   134  	return uint16(b[0]) + (uint16(b[1]) << 8)
   135  }