github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/syslinux/syslinux_test.go (about)

     1  // Copyright 2017-2018 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  package syslinux
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"net/url"
    14  	"os"
    15  	"path/filepath"
    16  	"reflect"
    17  	"strings"
    18  	"testing"
    19  
    20  	"github.com/mvdan/u-root-coreutils/pkg/boot"
    21  	"github.com/mvdan/u-root-coreutils/pkg/boot/boottest"
    22  	"github.com/mvdan/u-root-coreutils/pkg/boot/linux"
    23  	"github.com/mvdan/u-root-coreutils/pkg/boot/multiboot"
    24  	"github.com/mvdan/u-root-coreutils/pkg/curl"
    25  )
    26  
    27  func mustParseURL(s string) *url.URL {
    28  	u, err := url.Parse(s)
    29  	if err != nil {
    30  		panic(fmt.Sprintf("parsing %q failed: %v", s, err))
    31  	}
    32  	return u
    33  }
    34  
    35  type errorReader struct {
    36  	err error
    37  }
    38  
    39  func (e errorReader) ReadAt(p []byte, n int64) (int, error) {
    40  	return 0, e.err
    41  }
    42  
    43  func TestParseGeneral(t *testing.T) {
    44  	kernel1 := "kernel1"
    45  	kernel2 := "kernel2"
    46  	globalInitrd := "globalInitrd"
    47  	initrd1 := "initrd1"
    48  	initrd2 := "initrd2"
    49  	xengz := "xengz"
    50  	mboot := "mboot.c32"
    51  	boardDTB := "board.dtb"
    52  
    53  	newMockScheme := func() *curl.MockScheme {
    54  		fs := curl.NewMockScheme("tftp")
    55  		fs.Add("1.2.3.4", "/foobar/pxelinux.0", "")
    56  		fs.Add("1.2.3.4", "/foobar/pxefiles/kernel1", kernel1)
    57  		fs.Add("1.2.3.4", "/foobar/pxefiles/kernel2", kernel2)
    58  		fs.Add("1.2.3.4", "/foobar/pxefiles/global_initrd", globalInitrd)
    59  		fs.Add("1.2.3.4", "/foobar/pxefiles/initrd1", initrd1)
    60  		fs.Add("1.2.3.4", "/foobar/pxefiles/initrd2", initrd2)
    61  		fs.Add("1.2.3.4", "/foobar/pxefiles/board.dtb", boardDTB)
    62  		fs.Add("1.2.3.4", "/foobar/xen.gz", xengz)
    63  		fs.Add("1.2.3.4", "/foobar/mboot.c32", mboot)
    64  
    65  		fs.Add("2.3.4.5", "/barfoo/pxefiles/kernel1", kernel1)
    66  		return fs
    67  	}
    68  	http := curl.NewMockScheme("http")
    69  	http.Add("someplace.com", "/initrd2", initrd2)
    70  
    71  	for i, tt := range []struct {
    72  		desc        string
    73  		configFiles map[string]string
    74  		want        []boot.OSImage
    75  		err         error
    76  	}{
    77  		{
    78  			desc: "all files exist, simple config with cmdline initrd",
    79  			configFiles: map[string]string{
    80  				"/foobar/pxelinux.cfg/default": `
    81  					default foo
    82  					label foo
    83  					kernel ./pxefiles/kernel1
    84  					append initrd=./pxefiles/global_initrd`,
    85  			},
    86  			want: []boot.OSImage{
    87  				&boot.LinuxImage{
    88  					Name:    "foo",
    89  					Kernel:  strings.NewReader(kernel1),
    90  					Initrd:  strings.NewReader(globalInitrd),
    91  					Cmdline: "initrd=./pxefiles/global_initrd",
    92  				},
    93  			},
    94  		},
    95  		{
    96  			desc: "empty label",
    97  			configFiles: map[string]string{
    98  				"/foobar/pxelinux.cfg/default": `
    99  					default foo
   100  					label foo`,
   101  			},
   102  			want: nil,
   103  		},
   104  
   105  		{
   106  			desc: "all files exist, simple config with directive initrd",
   107  			configFiles: map[string]string{
   108  				"/foobar/pxelinux.cfg/default": `
   109  					default foo
   110  					label foo
   111  					kernel ./pxefiles/kernel1
   112  					initrd ./pxefiles/initrd1
   113  					append foo=bar`,
   114  			},
   115  			want: []boot.OSImage{
   116  				&boot.LinuxImage{
   117  					Name:    "foo",
   118  					Kernel:  strings.NewReader(kernel1),
   119  					Initrd:  strings.NewReader(initrd1),
   120  					Cmdline: "foo=bar",
   121  				},
   122  			},
   123  		},
   124  		{
   125  			desc: "all files exist, simple config, no initrd",
   126  			configFiles: map[string]string{
   127  				"/foobar/pxelinux.cfg/default": `
   128  					default foo
   129  					label foo
   130  					kernel ./pxefiles/kernel1`,
   131  			},
   132  			want: []boot.OSImage{
   133  				&boot.LinuxImage{
   134  					Name:    "foo",
   135  					Kernel:  strings.NewReader(kernel1),
   136  					Initrd:  nil,
   137  					Cmdline: "",
   138  				},
   139  			},
   140  		},
   141  		{
   142  			desc: "all files exist, simple config with two initrd files",
   143  			configFiles: map[string]string{
   144  				"/foobar/pxelinux.cfg/default": `
   145  					default foo
   146  					label multi-initrd
   147  					kernel ./pxefiles/kernel1
   148  					initrd ./pxefiles/initrd1,./pxefiles/initrd2
   149  					append foo=bar`,
   150  			},
   151  			want: []boot.OSImage{
   152  				&boot.LinuxImage{
   153  					Name:    "multi-initrd",
   154  					Kernel:  strings.NewReader(kernel1),
   155  					Initrd:  boot.CatInitrds(strings.NewReader(initrd1), strings.NewReader(initrd2)),
   156  					Cmdline: "foo=bar",
   157  				},
   158  			},
   159  		},
   160  		{
   161  			desc: "an initrd file missing, config with two initrd files",
   162  			configFiles: map[string]string{
   163  				"/foobar/pxelinux.cfg/default": `
   164  					default foo
   165  					label multi-initrd
   166  					kernel ./pxefiles/kernel1
   167  					initrd ./pxefiles/initrd1,./pxefiles/no-initrd-here
   168  					append foo=bar`,
   169  			},
   170  			want: []boot.OSImage{
   171  				&boot.LinuxImage{
   172  					Name:   "multi-initrd",
   173  					Kernel: strings.NewReader(kernel1),
   174  					Initrd: errorReader{&curl.URLError{
   175  						URL: &url.URL{
   176  							Scheme: "tftp",
   177  							Host:   "1.2.3.4",
   178  							Path:   "/foobar/pxefiles/no-initrd-here",
   179  						},
   180  						Err: curl.ErrNoSuchFile,
   181  					}},
   182  					Cmdline: "foo=bar",
   183  				},
   184  			},
   185  		},
   186  		{
   187  			desc: "kernel does not exist, simple config",
   188  			configFiles: map[string]string{
   189  				"/foobar/pxelinux.cfg/default": `
   190  					default foo
   191  					label foo
   192  					kernel ./pxefiles/does-not-exist`,
   193  			},
   194  			want: []boot.OSImage{
   195  				&boot.LinuxImage{
   196  					Name: "foo",
   197  					Kernel: errorReader{&curl.URLError{
   198  						URL: &url.URL{
   199  							Scheme: "tftp",
   200  							Host:   "1.2.3.4",
   201  							Path:   "/foobar/pxefiles/does-not-exist",
   202  						},
   203  						Err: curl.ErrNoSuchFile,
   204  					}},
   205  					Initrd:  nil,
   206  					Cmdline: "",
   207  				},
   208  			},
   209  		},
   210  		{
   211  			desc: "config file does not exist",
   212  			err: &curl.URLError{
   213  				URL: &url.URL{
   214  					Scheme: "tftp",
   215  					Host:   "1.2.3.4",
   216  					Path:   "/foobar/pxelinux.cfg/default",
   217  				},
   218  				Err: curl.ErrNoSuchFile,
   219  			},
   220  		},
   221  		{
   222  			desc: "empty config",
   223  			configFiles: map[string]string{
   224  				"/foobar/pxelinux.cfg/default": "",
   225  			},
   226  			want: nil,
   227  		},
   228  		{
   229  			desc: "valid config with two Entries",
   230  			configFiles: map[string]string{
   231  				"/foobar/pxelinux.cfg/default": `
   232  					default foo
   233  
   234  					label bar
   235  					menu label Bla Bla Bla
   236  					kernel ./pxefiles/kernel2
   237  					append console=ttyS0
   238  
   239  					label foo
   240  					kernel ./pxefiles/kernel1
   241  					append earlyprintk=ttyS0 printk=ttyS0`,
   242  			},
   243  			want: []boot.OSImage{
   244  				&boot.LinuxImage{
   245  					Name:    "foo",
   246  					Kernel:  strings.NewReader(kernel1),
   247  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   248  				},
   249  				&boot.LinuxImage{
   250  					Name:    "Bla Bla Bla",
   251  					Kernel:  strings.NewReader(kernel2),
   252  					Cmdline: "console=ttyS0",
   253  				},
   254  			},
   255  		},
   256  		{
   257  			desc: "menu default, linux directives",
   258  			configFiles: map[string]string{
   259  				"/foobar/pxelinux.cfg/default": `
   260  					label bar
   261  					menu label Bla Bla Bla
   262  					kernel ./pxefiles/kernel2
   263  					append console=ttyS0
   264  
   265  					label foo
   266  					menu default
   267  					linux ./pxefiles/kernel1
   268  					append earlyprintk=ttyS0 printk=ttyS0`,
   269  			},
   270  			want: []boot.OSImage{
   271  				&boot.LinuxImage{
   272  					Name:    "foo",
   273  					Kernel:  strings.NewReader(kernel1),
   274  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   275  				},
   276  				&boot.LinuxImage{
   277  					Name:    "Bla Bla Bla",
   278  					Kernel:  strings.NewReader(kernel2),
   279  					Cmdline: "console=ttyS0",
   280  				},
   281  			},
   282  		},
   283  		{
   284  			desc: "valid config with two Entries, and a nerfdefault override",
   285  			configFiles: map[string]string{
   286  				"/foobar/pxelinux.cfg/default": `
   287  					default foo
   288  
   289  					nerfdefault bar
   290  
   291  					label foo
   292  					kernel ./pxefiles/kernel1
   293  					append earlyprintk=ttyS0 printk=ttyS0
   294  
   295  					label bar
   296  					kernel ./pxefiles/kernel2
   297  					append console=ttyS0`,
   298  			},
   299  			want: []boot.OSImage{
   300  				&boot.LinuxImage{
   301  					Name:    "bar",
   302  					Kernel:  strings.NewReader(kernel2),
   303  					Cmdline: "console=ttyS0",
   304  				},
   305  				&boot.LinuxImage{
   306  					Name:    "foo",
   307  					Kernel:  strings.NewReader(kernel1),
   308  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   309  				},
   310  			},
   311  		},
   312  		{
   313  			desc: "valid config with two Entries, and a nerfdefault override, order agnostic",
   314  			configFiles: map[string]string{
   315  				"/foobar/pxelinux.cfg/default": `
   316  					nerfdefault bar
   317  
   318  					default foo
   319  
   320  					label foo
   321  					kernel ./pxefiles/kernel1
   322  					append earlyprintk=ttyS0 printk=ttyS0
   323  
   324  					label bar
   325  					kernel ./pxefiles/kernel2
   326  					append console=ttyS0`,
   327  			},
   328  			want: []boot.OSImage{
   329  				&boot.LinuxImage{
   330  					Name:    "bar",
   331  					Kernel:  strings.NewReader(kernel2),
   332  					Cmdline: "console=ttyS0",
   333  				},
   334  				&boot.LinuxImage{
   335  					Name:    "foo",
   336  					Kernel:  strings.NewReader(kernel1),
   337  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   338  				},
   339  			},
   340  		},
   341  
   342  		{
   343  			desc: "valid config with global APPEND directive",
   344  			configFiles: map[string]string{
   345  				"/foobar/pxelinux.cfg/default": `
   346  					default foo
   347  					append foo=bar
   348  
   349  					label foo
   350  					kernel ./pxefiles/kernel1
   351  					append earlyprintk=ttyS0 printk=ttyS0
   352  
   353  					label bar
   354  					kernel ./pxefiles/kernel2
   355  
   356  					label baz
   357  					kernel ./pxefiles/kernel2
   358  					append -`,
   359  			},
   360  			want: []boot.OSImage{
   361  				&boot.LinuxImage{
   362  					Name:   "foo",
   363  					Kernel: strings.NewReader(kernel1),
   364  					// Does not contain global APPEND.
   365  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   366  				},
   367  				&boot.LinuxImage{
   368  					Name:   "bar",
   369  					Kernel: strings.NewReader(kernel2),
   370  					// Contains only global APPEND.
   371  					Cmdline: "foo=bar",
   372  				},
   373  				&boot.LinuxImage{
   374  					Name:   "baz",
   375  					Kernel: strings.NewReader(kernel2),
   376  					// "APPEND -" means ignore global APPEND.
   377  					Cmdline: "",
   378  				},
   379  			},
   380  		},
   381  		{
   382  			desc: "valid config with global APPEND with initrd",
   383  			configFiles: map[string]string{
   384  				"/foobar/pxelinux.cfg/default": `
   385  					default mcnulty
   386  					append initrd=./pxefiles/global_initrd
   387  
   388  					label mcnulty
   389  					kernel ./pxefiles/kernel1
   390  					append earlyprintk=ttyS0 printk=ttyS0
   391  
   392  					label lester
   393  					kernel ./pxefiles/kernel1
   394  
   395  					label omar
   396  					kernel ./pxefiles/kernel2
   397  					append -
   398  
   399  					label stringer
   400  					kernel ./pxefiles/kernel2
   401  					initrd ./pxefiles/initrd2
   402  				`,
   403  			},
   404  			want: []boot.OSImage{
   405  				&boot.LinuxImage{
   406  					Name:   "mcnulty",
   407  					Kernel: strings.NewReader(kernel1),
   408  					// Does not contain global APPEND.
   409  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   410  				},
   411  				&boot.LinuxImage{
   412  					Name:   "lester",
   413  					Kernel: strings.NewReader(kernel1),
   414  					Initrd: strings.NewReader(globalInitrd),
   415  					// Contains only global APPEND.
   416  					Cmdline: "initrd=./pxefiles/global_initrd",
   417  				},
   418  				&boot.LinuxImage{
   419  					Name:   "omar",
   420  					Kernel: strings.NewReader(kernel2),
   421  					// "APPEND -" means ignore global APPEND.
   422  					Cmdline: "",
   423  				},
   424  				&boot.LinuxImage{
   425  					Name:   "stringer",
   426  					Kernel: strings.NewReader(kernel2),
   427  					Initrd: strings.NewReader(initrd2),
   428  
   429  					// TODO: See syslinux initrd handling. This SHOULD be
   430  					//
   431  					// initrd=./pxefiles/global_initrd initrd=./pxefiles/initrd2
   432  					//
   433  					// https://wiki.syslinux.org/wiki/index.php?title=Directives/append
   434  					Cmdline: "initrd=./pxefiles/global_initrd",
   435  				},
   436  			},
   437  		},
   438  		{
   439  			desc: "default label does not exist",
   440  			configFiles: map[string]string{
   441  				"/foobar/pxelinux.cfg/default": `default not-exist`,
   442  			},
   443  			want: nil,
   444  		},
   445  		{
   446  			desc: "multi-scheme valid config",
   447  			configFiles: map[string]string{
   448  				"/foobar/pxelinux.cfg/default": `
   449  				default sheeeit
   450  
   451  				label sheeeit
   452  				kernel ./pxefiles/kernel2
   453  				initrd http://someplace.com/initrd2`,
   454  			},
   455  			want: []boot.OSImage{
   456  				&boot.LinuxImage{
   457  					Name:   "sheeeit",
   458  					Kernel: strings.NewReader(kernel2),
   459  					Initrd: strings.NewReader(initrd2),
   460  				},
   461  			},
   462  		},
   463  		{
   464  			desc: "valid config with three includes",
   465  			configFiles: map[string]string{
   466  				"/foobar/pxelinux.cfg/default": `
   467  					default mcnulty
   468  
   469  					include installer/txt.cfg
   470  					include installer/stdmenu.cfg
   471  
   472  					menu begin advanced
   473  					  menu title Advanced Options
   474  					  include installer/stdmenu.cfg
   475  					menu end
   476  				`,
   477  
   478  				"/foobar/installer/txt.cfg": `
   479  					label mcnulty
   480  					kernel ./pxefiles/kernel1
   481  					append earlyprintk=ttyS0 printk=ttyS0
   482  				`,
   483  
   484  				"/foobar/installer/stdmenu.cfg": `
   485  					label omar
   486  					kernel ./pxefiles/kernel2
   487  				`,
   488  			},
   489  			want: []boot.OSImage{
   490  				&boot.LinuxImage{
   491  					Name:    "mcnulty",
   492  					Kernel:  strings.NewReader(kernel1),
   493  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   494  				},
   495  				&boot.LinuxImage{
   496  					Name:   "omar",
   497  					Kernel: strings.NewReader(kernel2),
   498  				},
   499  			},
   500  		},
   501  		{
   502  			desc: "multiboot images",
   503  			configFiles: map[string]string{
   504  				"/foobar/pxelinux.cfg/default": `
   505  					default foo
   506  
   507  					label bar
   508  					menu label Bla Bla Bla
   509  					kernel mboot.c32
   510  					append xen.gz console=none --- ./pxefiles/kernel1 foobar hahaha --- ./pxefiles/initrd1
   511  
   512  					label mbootnomodules
   513  					kernel mboot.c32
   514  					append xen.gz
   515  
   516  					label foo
   517  					linux mboot.c32
   518  					append earlyprintk=ttyS0 printk=ttyS0`,
   519  			},
   520  			want: []boot.OSImage{
   521  				&boot.LinuxImage{
   522  					Name:    "foo",
   523  					Kernel:  strings.NewReader(mboot),
   524  					Cmdline: "earlyprintk=ttyS0 printk=ttyS0",
   525  				},
   526  				&boot.MultibootImage{
   527  					Name:    "Bla Bla Bla",
   528  					Kernel:  strings.NewReader(xengz),
   529  					Cmdline: "console=none",
   530  					Modules: []multiboot.Module{
   531  						{
   532  							Module:  strings.NewReader(kernel1),
   533  							Cmdline: "./pxefiles/kernel1 foobar hahaha",
   534  						},
   535  						{
   536  							Module:  strings.NewReader(initrd1),
   537  							Cmdline: "./pxefiles/initrd1",
   538  						},
   539  					},
   540  				},
   541  				&boot.MultibootImage{
   542  					Name:   "mbootnomodules",
   543  					Kernel: strings.NewReader(xengz),
   544  				},
   545  			},
   546  		},
   547  		{
   548  			desc: "simple config with all required file and fdt",
   549  			configFiles: map[string]string{
   550  				"/foobar/pxelinux.cfg/default": `
   551  					default foo
   552  					label foo
   553  					kernel ./pxefiles/kernel1
   554  					initrd ./pxefiles/global_initrd
   555  					append foo=bar
   556  					fdt ./pxefiles/board.dtb`,
   557  			},
   558  			want: []boot.OSImage{
   559  				&boot.LinuxImage{
   560  					Name:      "foo",
   561  					Kernel:    strings.NewReader(kernel1),
   562  					Initrd:    strings.NewReader(globalInitrd),
   563  					Cmdline:   "foo=bar",
   564  					KexecOpts: linux.KexecOptions{},
   565  				},
   566  			},
   567  		},
   568  	} {
   569  		t.Run(fmt.Sprintf("Test [%02d] %s", i, tt.desc), func(t *testing.T) {
   570  			fs := newMockScheme()
   571  			for filename, content := range tt.configFiles {
   572  				fs.Add("1.2.3.4", filename, content)
   573  			}
   574  			s := make(curl.Schemes)
   575  			s.Register(fs.Scheme, fs)
   576  			s.Register(http.Scheme, http)
   577  
   578  			rootdir := &url.URL{
   579  				Scheme: "tftp",
   580  				Host:   "1.2.3.4",
   581  				Path:   "/",
   582  			}
   583  
   584  			got, err := ParseConfigFile(context.Background(), s, "pxelinux.cfg/default", rootdir, "foobar")
   585  			if !reflect.DeepEqual(err, tt.err) {
   586  				t.Errorf("AppendFile() got %v, want %v", err, tt.err)
   587  			} else if err != nil {
   588  				return
   589  			}
   590  
   591  			if len(tt.want) != len(got) {
   592  				t.Errorf("ParseConfigFile yielded %d images, want %d images", len(got), len(tt.want))
   593  			}
   594  
   595  			for i, want := range tt.want {
   596  				if err := boottest.SameBootImage(got[i], want); err != nil {
   597  					t.Errorf("Boot image index %d not same: %v", i, err)
   598  				}
   599  			}
   600  		})
   601  	}
   602  }
   603  
   604  func TestParseCorner(t *testing.T) {
   605  	for _, tt := range []struct {
   606  		name       string
   607  		s          curl.Schemes
   608  		configFile string
   609  		rootdir    *url.URL
   610  		wd         string
   611  		err        error
   612  	}{
   613  		{
   614  			name:       "no schemes",
   615  			s:          nil,
   616  			configFile: "pxelinux.cfg/default",
   617  			rootdir: &url.URL{
   618  				Scheme: "tftp",
   619  				Host:   "1.2.3.4",
   620  				Path:   "/foobar",
   621  			},
   622  			err: &curl.URLError{
   623  				URL: &url.URL{
   624  					Scheme: "tftp",
   625  					Host:   "1.2.3.4",
   626  					Path:   "/foobar/pxelinux.cfg/default",
   627  				},
   628  				Err: curl.ErrNoSuchScheme,
   629  			},
   630  		},
   631  		{
   632  			name:       "no scheme and config file",
   633  			s:          nil,
   634  			configFile: "",
   635  			rootdir: &url.URL{
   636  				Scheme: "tftp",
   637  				Host:   "1.2.3.4",
   638  				Path:   "/foobar",
   639  			},
   640  			err: &curl.URLError{
   641  				URL: &url.URL{
   642  					Scheme: "tftp",
   643  					Host:   "1.2.3.4",
   644  					Path:   "/foobar",
   645  				},
   646  				Err: curl.ErrNoSuchScheme,
   647  			},
   648  		},
   649  		{
   650  			name:       "no scheme, config file, and working dir",
   651  			s:          nil,
   652  			configFile: "",
   653  			rootdir:    nil,
   654  			err: &curl.URLError{
   655  				URL: &url.URL{},
   656  				Err: curl.ErrNoSuchScheme,
   657  			},
   658  		},
   659  	} {
   660  		t.Run(tt.name, func(t *testing.T) {
   661  			_, err := ParseConfigFile(context.Background(), tt.s, tt.configFile, tt.rootdir, tt.wd)
   662  			if !reflect.DeepEqual(err, tt.err) {
   663  				t.Errorf("ParseConfigFile() = %v, want %v", err, tt.err)
   664  			}
   665  		})
   666  	}
   667  }
   668  
   669  func TestParseURL(t *testing.T) {
   670  	for _, tt := range []struct {
   671  		filename string
   672  		rootdir  *url.URL
   673  		wd       string
   674  		want     *url.URL
   675  	}{
   676  		{
   677  			filename: "foobar",
   678  			rootdir:  mustParseURL("http://[2001::1]:18282/"),
   679  			wd:       "files/more",
   680  			want:     mustParseURL("http://[2001::1]:18282/files/more/foobar"),
   681  		},
   682  		{
   683  			filename: "/foobar",
   684  			rootdir:  mustParseURL("http://[2001::1]:18282"),
   685  			wd:       "files/more",
   686  			want:     mustParseURL("http://[2001::1]:18282/foobar"),
   687  		},
   688  		{
   689  			filename: "http://[2002::2]/blabla",
   690  			rootdir:  mustParseURL("http://[2001::1]:18282/files"),
   691  			wd:       "more",
   692  			want:     mustParseURL("http://[2002::2]/blabla"),
   693  		},
   694  		{
   695  			filename: "http://[2002::2]/blabla",
   696  			rootdir:  nil,
   697  			want:     mustParseURL("http://[2002::2]/blabla"),
   698  		},
   699  	} {
   700  		got, err := parseURL(tt.filename, tt.rootdir, tt.wd)
   701  		if err != nil {
   702  			t.Errorf("parseURL(%q, %s, %s) = %v, want %v", tt.filename, tt.rootdir, tt.wd, err, nil)
   703  		}
   704  
   705  		if !reflect.DeepEqual(got, tt.want) {
   706  			t.Errorf("parseURL(%q, %s, %s) = %v, want %v", tt.filename, tt.rootdir, tt.wd, got, tt.want)
   707  		}
   708  	}
   709  }
   710  
   711  func FuzzParseSyslinuxConfig(f *testing.F) {
   712  
   713  	dirPath := f.TempDir()
   714  
   715  	path := filepath.Join(dirPath, "isolinux.cfg")
   716  
   717  	log.SetOutput(io.Discard)
   718  	log.SetFlags(0)
   719  
   720  	// get seed corpora from testdata_new files
   721  	seeds, err := filepath.Glob("testdata/*/*/isolinux.cfg")
   722  	if err != nil {
   723  		f.Fatalf("failed to find seed corpora files: %v", err)
   724  	}
   725  
   726  	for _, seed := range seeds {
   727  		seedBytes, err := os.ReadFile(seed)
   728  		if err != nil {
   729  			f.Fatalf("failed read seed corpora from files %v: %v", seed, err)
   730  		}
   731  
   732  		f.Add(seedBytes)
   733  	}
   734  
   735  	f.Add([]byte("lABel 0\nAppend initrd"))
   736  	f.Add([]byte("lABel 0\nkernel mboot.c32\nAppend ---"))
   737  	f.Fuzz(func(t *testing.T, data []byte) {
   738  
   739  		if len(data) > 4096 {
   740  			return
   741  		}
   742  
   743  		// do not allow arbitrary files reads
   744  		if bytes.Contains(data, []byte("include")) {
   745  			return
   746  		}
   747  
   748  		err := os.WriteFile(path, data, 0o777)
   749  		if err != nil {
   750  			t.Fatalf("Failed to create configfile '%v':%v", path, err)
   751  		}
   752  
   753  		ParseLocalConfig(context.Background(), dirPath)
   754  	})
   755  
   756  }