github.hscsec.cn/u-root/u-root@v7.0.0+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: "all files exist, simple config with two initrd files", 135 configFiles: map[string]string{ 136 "/foobar/pxelinux.cfg/default": ` 137 default foo 138 label multi-initrd 139 kernel ./pxefiles/kernel1 140 initrd ./pxefiles/initrd1,./pxefiles/initrd2 141 append foo=bar`, 142 }, 143 want: []boot.OSImage{ 144 &boot.LinuxImage{ 145 Name: "multi-initrd", 146 Kernel: strings.NewReader(kernel1), 147 Initrd: boot.CatInitrds(strings.NewReader(initrd1), strings.NewReader(initrd2)), 148 Cmdline: "foo=bar", 149 }, 150 }, 151 }, 152 { 153 desc: "an initrd file missing, config with two initrd files", 154 configFiles: map[string]string{ 155 "/foobar/pxelinux.cfg/default": ` 156 default foo 157 label multi-initrd 158 kernel ./pxefiles/kernel1 159 initrd ./pxefiles/initrd1,./pxefiles/no-initrd-here 160 append foo=bar`, 161 }, 162 want: []boot.OSImage{ 163 &boot.LinuxImage{ 164 Name: "multi-initrd", 165 Kernel: strings.NewReader(kernel1), 166 Initrd: errorReader{&curl.URLError{ 167 URL: &url.URL{ 168 Scheme: "tftp", 169 Host: "1.2.3.4", 170 Path: "/foobar/pxefiles/no-initrd-here", 171 }, 172 Err: curl.ErrNoSuchFile, 173 }}, 174 Cmdline: "foo=bar", 175 }, 176 }, 177 }, 178 { 179 desc: "kernel does not exist, simple config", 180 configFiles: map[string]string{ 181 "/foobar/pxelinux.cfg/default": ` 182 default foo 183 label foo 184 kernel ./pxefiles/does-not-exist`, 185 }, 186 want: []boot.OSImage{ 187 &boot.LinuxImage{ 188 Name: "foo", 189 Kernel: errorReader{&curl.URLError{ 190 URL: &url.URL{ 191 Scheme: "tftp", 192 Host: "1.2.3.4", 193 Path: "/foobar/pxefiles/does-not-exist", 194 }, 195 Err: curl.ErrNoSuchFile, 196 }}, 197 Initrd: nil, 198 Cmdline: "", 199 }, 200 }, 201 }, 202 { 203 desc: "config file does not exist", 204 err: &curl.URLError{ 205 URL: &url.URL{ 206 Scheme: "tftp", 207 Host: "1.2.3.4", 208 Path: "/foobar/pxelinux.cfg/default", 209 }, 210 Err: curl.ErrNoSuchFile, 211 }, 212 }, 213 { 214 desc: "empty config", 215 configFiles: map[string]string{ 216 "/foobar/pxelinux.cfg/default": "", 217 }, 218 want: nil, 219 }, 220 { 221 desc: "valid config with two Entries", 222 configFiles: map[string]string{ 223 "/foobar/pxelinux.cfg/default": ` 224 default foo 225 226 label bar 227 menu label Bla Bla Bla 228 kernel ./pxefiles/kernel2 229 append console=ttyS0 230 231 label foo 232 kernel ./pxefiles/kernel1 233 append earlyprintk=ttyS0 printk=ttyS0`, 234 }, 235 want: []boot.OSImage{ 236 &boot.LinuxImage{ 237 Name: "foo", 238 Kernel: strings.NewReader(kernel1), 239 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 240 }, 241 &boot.LinuxImage{ 242 Name: "Bla Bla Bla", 243 Kernel: strings.NewReader(kernel2), 244 Cmdline: "console=ttyS0", 245 }, 246 }, 247 }, 248 { 249 desc: "menu default, linux directives", 250 configFiles: map[string]string{ 251 "/foobar/pxelinux.cfg/default": ` 252 label bar 253 menu label Bla Bla Bla 254 kernel ./pxefiles/kernel2 255 append console=ttyS0 256 257 label foo 258 menu default 259 linux ./pxefiles/kernel1 260 append earlyprintk=ttyS0 printk=ttyS0`, 261 }, 262 want: []boot.OSImage{ 263 &boot.LinuxImage{ 264 Name: "foo", 265 Kernel: strings.NewReader(kernel1), 266 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 267 }, 268 &boot.LinuxImage{ 269 Name: "Bla Bla Bla", 270 Kernel: strings.NewReader(kernel2), 271 Cmdline: "console=ttyS0", 272 }, 273 }, 274 }, 275 { 276 desc: "valid config with two Entries, and a nerfdefault override", 277 configFiles: map[string]string{ 278 "/foobar/pxelinux.cfg/default": ` 279 default foo 280 281 nerfdefault bar 282 283 label foo 284 kernel ./pxefiles/kernel1 285 append earlyprintk=ttyS0 printk=ttyS0 286 287 label bar 288 kernel ./pxefiles/kernel2 289 append console=ttyS0`, 290 }, 291 want: []boot.OSImage{ 292 &boot.LinuxImage{ 293 Name: "bar", 294 Kernel: strings.NewReader(kernel2), 295 Cmdline: "console=ttyS0", 296 }, 297 &boot.LinuxImage{ 298 Name: "foo", 299 Kernel: strings.NewReader(kernel1), 300 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 301 }, 302 }, 303 }, 304 { 305 desc: "valid config with two Entries, and a nerfdefault override, order agnostic", 306 configFiles: map[string]string{ 307 "/foobar/pxelinux.cfg/default": ` 308 nerfdefault bar 309 310 default foo 311 312 label foo 313 kernel ./pxefiles/kernel1 314 append earlyprintk=ttyS0 printk=ttyS0 315 316 label bar 317 kernel ./pxefiles/kernel2 318 append console=ttyS0`, 319 }, 320 want: []boot.OSImage{ 321 &boot.LinuxImage{ 322 Name: "bar", 323 Kernel: strings.NewReader(kernel2), 324 Cmdline: "console=ttyS0", 325 }, 326 &boot.LinuxImage{ 327 Name: "foo", 328 Kernel: strings.NewReader(kernel1), 329 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 330 }, 331 }, 332 }, 333 334 { 335 desc: "valid config with global APPEND directive", 336 configFiles: map[string]string{ 337 "/foobar/pxelinux.cfg/default": ` 338 default foo 339 append foo=bar 340 341 label foo 342 kernel ./pxefiles/kernel1 343 append earlyprintk=ttyS0 printk=ttyS0 344 345 label bar 346 kernel ./pxefiles/kernel2 347 348 label baz 349 kernel ./pxefiles/kernel2 350 append -`, 351 }, 352 want: []boot.OSImage{ 353 &boot.LinuxImage{ 354 Name: "foo", 355 Kernel: strings.NewReader(kernel1), 356 // Does not contain global APPEND. 357 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 358 }, 359 &boot.LinuxImage{ 360 Name: "bar", 361 Kernel: strings.NewReader(kernel2), 362 // Contains only global APPEND. 363 Cmdline: "foo=bar", 364 }, 365 &boot.LinuxImage{ 366 Name: "baz", 367 Kernel: strings.NewReader(kernel2), 368 // "APPEND -" means ignore global APPEND. 369 Cmdline: "", 370 }, 371 }, 372 }, 373 { 374 desc: "valid config with global APPEND with initrd", 375 configFiles: map[string]string{ 376 "/foobar/pxelinux.cfg/default": ` 377 default mcnulty 378 append initrd=./pxefiles/global_initrd 379 380 label mcnulty 381 kernel ./pxefiles/kernel1 382 append earlyprintk=ttyS0 printk=ttyS0 383 384 label lester 385 kernel ./pxefiles/kernel1 386 387 label omar 388 kernel ./pxefiles/kernel2 389 append - 390 391 label stringer 392 kernel ./pxefiles/kernel2 393 initrd ./pxefiles/initrd2 394 `, 395 }, 396 want: []boot.OSImage{ 397 &boot.LinuxImage{ 398 Name: "mcnulty", 399 Kernel: strings.NewReader(kernel1), 400 // Does not contain global APPEND. 401 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 402 }, 403 &boot.LinuxImage{ 404 Name: "lester", 405 Kernel: strings.NewReader(kernel1), 406 Initrd: strings.NewReader(globalInitrd), 407 // Contains only global APPEND. 408 Cmdline: "initrd=./pxefiles/global_initrd", 409 }, 410 &boot.LinuxImage{ 411 Name: "omar", 412 Kernel: strings.NewReader(kernel2), 413 // "APPEND -" means ignore global APPEND. 414 Cmdline: "", 415 }, 416 &boot.LinuxImage{ 417 Name: "stringer", 418 Kernel: strings.NewReader(kernel2), 419 Initrd: strings.NewReader(initrd2), 420 421 // TODO: See syslinux initrd handling. This SHOULD be 422 // 423 // initrd=./pxefiles/global_initrd initrd=./pxefiles/initrd2 424 // 425 // https://wiki.syslinux.org/wiki/index.php?title=Directives/append 426 Cmdline: "initrd=./pxefiles/global_initrd", 427 }, 428 }, 429 }, 430 { 431 desc: "default label does not exist", 432 configFiles: map[string]string{ 433 "/foobar/pxelinux.cfg/default": `default not-exist`, 434 }, 435 want: nil, 436 }, 437 { 438 desc: "multi-scheme valid config", 439 configFiles: map[string]string{ 440 "/foobar/pxelinux.cfg/default": ` 441 default sheeeit 442 443 label sheeeit 444 kernel ./pxefiles/kernel2 445 initrd http://someplace.com/initrd2`, 446 }, 447 want: []boot.OSImage{ 448 &boot.LinuxImage{ 449 Name: "sheeeit", 450 Kernel: strings.NewReader(kernel2), 451 Initrd: strings.NewReader(initrd2), 452 }, 453 }, 454 }, 455 { 456 desc: "valid config with three includes", 457 configFiles: map[string]string{ 458 "/foobar/pxelinux.cfg/default": ` 459 default mcnulty 460 461 include installer/txt.cfg 462 include installer/stdmenu.cfg 463 464 menu begin advanced 465 menu title Advanced Options 466 include installer/stdmenu.cfg 467 menu end 468 `, 469 470 "/foobar/installer/txt.cfg": ` 471 label mcnulty 472 kernel ./pxefiles/kernel1 473 append earlyprintk=ttyS0 printk=ttyS0 474 `, 475 476 "/foobar/installer/stdmenu.cfg": ` 477 label omar 478 kernel ./pxefiles/kernel2 479 `, 480 }, 481 want: []boot.OSImage{ 482 &boot.LinuxImage{ 483 Name: "mcnulty", 484 Kernel: strings.NewReader(kernel1), 485 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 486 }, 487 &boot.LinuxImage{ 488 Name: "omar", 489 Kernel: strings.NewReader(kernel2), 490 }, 491 }, 492 }, 493 { 494 desc: "multiboot images", 495 configFiles: map[string]string{ 496 "/foobar/pxelinux.cfg/default": ` 497 default foo 498 499 label bar 500 menu label Bla Bla Bla 501 kernel mboot.c32 502 append xen.gz console=none --- ./pxefiles/kernel1 foobar hahaha --- ./pxefiles/initrd1 503 504 label mbootnomodules 505 kernel mboot.c32 506 append xen.gz 507 508 label foo 509 linux mboot.c32 510 append earlyprintk=ttyS0 printk=ttyS0`, 511 }, 512 want: []boot.OSImage{ 513 &boot.LinuxImage{ 514 Name: "foo", 515 Kernel: strings.NewReader(mboot), 516 Cmdline: "earlyprintk=ttyS0 printk=ttyS0", 517 }, 518 &boot.MultibootImage{ 519 Name: "Bla Bla Bla", 520 Kernel: strings.NewReader(xengz), 521 Cmdline: "console=none", 522 Modules: []multiboot.Module{ 523 { 524 Module: strings.NewReader(kernel1), 525 Cmdline: "./pxefiles/kernel1 foobar hahaha", 526 }, 527 { 528 Module: strings.NewReader(initrd1), 529 Cmdline: "./pxefiles/initrd1", 530 }, 531 }, 532 }, 533 &boot.MultibootImage{ 534 Name: "mbootnomodules", 535 Kernel: strings.NewReader(xengz), 536 }, 537 }, 538 }, 539 } { 540 t.Run(fmt.Sprintf("Test [%02d] %s", i, tt.desc), func(t *testing.T) { 541 fs := newMockScheme() 542 for filename, content := range tt.configFiles { 543 fs.Add("1.2.3.4", filename, content) 544 } 545 s := make(curl.Schemes) 546 s.Register(fs.Scheme, fs) 547 s.Register(http.Scheme, http) 548 549 rootdir := &url.URL{ 550 Scheme: "tftp", 551 Host: "1.2.3.4", 552 Path: "/", 553 } 554 555 got, err := ParseConfigFile(context.Background(), s, "pxelinux.cfg/default", rootdir, "foobar") 556 if !reflect.DeepEqual(err, tt.err) { 557 t.Errorf("AppendFile() got %v, want %v", err, tt.err) 558 } else if err != nil { 559 return 560 } 561 562 if len(tt.want) != len(got) { 563 t.Errorf("ParseConfigFile yielded %d images, want %d images", len(got), len(tt.want)) 564 } 565 566 for i, want := range tt.want { 567 if err := boottest.SameBootImage(got[i], want); err != nil { 568 t.Errorf("Boot image index %d not same: %v", i, err) 569 } 570 } 571 }) 572 } 573 } 574 575 func TestParseCorner(t *testing.T) { 576 for _, tt := range []struct { 577 name string 578 s curl.Schemes 579 configFile string 580 rootdir *url.URL 581 wd string 582 err error 583 }{ 584 { 585 name: "no schemes", 586 s: nil, 587 configFile: "pxelinux.cfg/default", 588 rootdir: &url.URL{ 589 Scheme: "tftp", 590 Host: "1.2.3.4", 591 Path: "/foobar", 592 }, 593 err: &curl.URLError{ 594 URL: &url.URL{ 595 Scheme: "tftp", 596 Host: "1.2.3.4", 597 Path: "/foobar/pxelinux.cfg/default", 598 }, 599 Err: curl.ErrNoSuchScheme, 600 }, 601 }, 602 { 603 name: "no scheme and config file", 604 s: nil, 605 configFile: "", 606 rootdir: &url.URL{ 607 Scheme: "tftp", 608 Host: "1.2.3.4", 609 Path: "/foobar", 610 }, 611 err: &curl.URLError{ 612 URL: &url.URL{ 613 Scheme: "tftp", 614 Host: "1.2.3.4", 615 Path: "/foobar", 616 }, 617 Err: curl.ErrNoSuchScheme, 618 }, 619 }, 620 { 621 name: "no scheme, config file, and working dir", 622 s: nil, 623 configFile: "", 624 rootdir: nil, 625 err: &curl.URLError{ 626 URL: &url.URL{}, 627 Err: curl.ErrNoSuchScheme, 628 }, 629 }, 630 } { 631 t.Run(tt.name, func(t *testing.T) { 632 _, err := ParseConfigFile(context.Background(), tt.s, tt.configFile, tt.rootdir, tt.wd) 633 if !reflect.DeepEqual(err, tt.err) { 634 t.Errorf("ParseConfigFile() = %v, want %v", err, tt.err) 635 } 636 }) 637 } 638 } 639 640 func TestParseURL(t *testing.T) { 641 for _, tt := range []struct { 642 filename string 643 rootdir *url.URL 644 wd string 645 want *url.URL 646 }{ 647 { 648 filename: "foobar", 649 rootdir: mustParseURL("http://[2001::1]:18282/"), 650 wd: "files/more", 651 want: mustParseURL("http://[2001::1]:18282/files/more/foobar"), 652 }, 653 { 654 filename: "/foobar", 655 rootdir: mustParseURL("http://[2001::1]:18282"), 656 wd: "files/more", 657 want: mustParseURL("http://[2001::1]:18282/foobar"), 658 }, 659 { 660 filename: "http://[2002::2]/blabla", 661 rootdir: mustParseURL("http://[2001::1]:18282/files"), 662 wd: "more", 663 want: mustParseURL("http://[2002::2]/blabla"), 664 }, 665 { 666 filename: "http://[2002::2]/blabla", 667 rootdir: nil, 668 want: mustParseURL("http://[2002::2]/blabla"), 669 }, 670 } { 671 got, err := parseURL(tt.filename, tt.rootdir, tt.wd) 672 if err != nil { 673 t.Errorf("parseURL(%q, %s, %s) = %v, want %v", tt.filename, tt.rootdir, tt.wd, err, nil) 674 } 675 676 if !reflect.DeepEqual(got, tt.want) { 677 t.Errorf("parseURL(%q, %s, %s) = %v, want %v", tt.filename, tt.rootdir, tt.wd, got, tt.want) 678 } 679 } 680 }