github.com/linuxboot/fiano@v1.2.0/pkg/visitors/remove.go (about)

     1  // Copyright 2018 the LinuxBoot 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  package visitors
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  
    12  	"github.com/linuxboot/fiano/pkg/uefi"
    13  )
    14  
    15  // Remove all firmware files with the given GUID.
    16  type Remove struct {
    17  	// Input
    18  	Predicate  func(f uefi.Firmware) bool
    19  	Pad        bool
    20  	RemoveDxes bool // I hate this, but there's no good way to work around our current structure
    21  
    22  	// Output
    23  	Matches []uefi.Firmware
    24  	// Calling this function undoes the removals performed by this visitor.
    25  	Undo func()
    26  	// logs are written to this writer.
    27  	W io.Writer
    28  }
    29  
    30  func (v *Remove) printf(format string, a ...interface{}) {
    31  	if v.W != nil {
    32  		fmt.Fprintf(v.W, format, a...)
    33  	}
    34  }
    35  
    36  // Run wraps Visit and performs some setup and teardown tasks.
    37  func (v *Remove) Run(f uefi.Firmware) error {
    38  	// First run "find" to generate a list of matches to delete.
    39  	find := Find{
    40  		Predicate: v.Predicate,
    41  	}
    42  	if v.RemoveDxes {
    43  		dxeFV, err := FindDXEFV(f)
    44  		if err != nil {
    45  			return err
    46  		}
    47  		if err := find.Run(dxeFV); err != nil {
    48  			return err
    49  		}
    50  		// We've found all the files in the blacklist
    51  		// now, we invert the matches
    52  		// Note, we can't use the NotPredicate here because that will match the
    53  		// sections of all files even if they are supposed to be excluded.
    54  		// This is terrible and my fault.
    55  		newMatches := []uefi.Firmware{}
    56  		for _, file := range dxeFV.Files {
    57  			var keep bool
    58  			for _, match := range find.Matches {
    59  				if match == file {
    60  					keep = true
    61  					break
    62  				}
    63  			}
    64  			if !keep {
    65  				newMatches = append(newMatches, file)
    66  			}
    67  		}
    68  
    69  		// Use this list of matches when removing files.
    70  		v.Matches = newMatches
    71  		return dxeFV.Apply(v)
    72  	}
    73  	if err := find.Run(f); err != nil {
    74  		return err
    75  	}
    76  
    77  	// Use this list of matches when removing files.
    78  	v.Matches = find.Matches
    79  	return f.Apply(v)
    80  }
    81  
    82  // Visit applies the Remove visitor to any Firmware type.
    83  func (v *Remove) Visit(f uefi.Firmware) error {
    84  	switch f := f.(type) {
    85  	case *uefi.FirmwareVolume:
    86  		for i := 0; i < len(f.Files); i++ {
    87  			for _, m := range v.Matches {
    88  				if f.Files[i] == m {
    89  					originalList := append([]*uefi.File{}, f.Files...)
    90  
    91  					m := m.(*uefi.File)
    92  					if v.Pad || m.Header.Type == uefi.FVFileTypePEIM {
    93  						// Create a new pad file of the exact same size
    94  						pf, err := uefi.CreatePadFile(m.Header.ExtendedSize)
    95  						if err != nil {
    96  							return err
    97  						}
    98  						f.Files[i] = pf
    99  					} else {
   100  						f.Files = append(f.Files[:i], f.Files[i+1:]...)
   101  					}
   102  					v.printf("Remove: %d files now\n", len(f.Files))
   103  
   104  					// Creates a stack of undoes in case there are multiple FVs.
   105  					prev := v.Undo
   106  					v.Undo = func() {
   107  						f.Files = originalList
   108  						v.printf("Undo: %d files now\n", len(f.Files))
   109  						v.Undo = prev
   110  					}
   111  				}
   112  			}
   113  		}
   114  	}
   115  
   116  	return f.ApplyChildren(v)
   117  }
   118  
   119  func init() {
   120  	RegisterCLI("remove", "remove a file from the volume", 1, func(args []string) (uefi.Visitor, error) {
   121  		pred, err := FindFilePredicate(args[0])
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  		return &Remove{
   126  			Predicate: pred,
   127  			Pad:       false,
   128  			W:         os.Stdout,
   129  		}, nil
   130  	})
   131  	RegisterCLI("remove_pad", "remove a file from the volume and replace it with a pad file of the same size", 1, func(args []string) (uefi.Visitor, error) {
   132  		pred, err := FindFilePredicate(args[0])
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		return &Remove{
   137  			Predicate: pred,
   138  			Pad:       true,
   139  			W:         os.Stdout,
   140  		}, nil
   141  	})
   142  	RegisterCLI("remove_dxes_except", "remove all files from the volume except those in the specified file", 1, func(args []string) (uefi.Visitor, error) {
   143  		fileName := args[0]
   144  		fileContents, err := os.ReadFile(fileName)
   145  		if err != nil {
   146  			return nil, fmt.Errorf("cannot read blacklist file %q: %v", fileName, err)
   147  		}
   148  		blackListRegex, err := parseBlackList(fileName, string(fileContents))
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  		blackListPredicate, err := FindFilePredicate(blackListRegex)
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  		pred := blackListPredicate
   157  
   158  		return &Remove{
   159  			Predicate:  pred,
   160  			RemoveDxes: true,
   161  		}, nil
   162  	})
   163  }