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