github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/bootloader/grub_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package bootloader_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "os/exec" 27 "path/filepath" 28 29 "github.com/mvo5/goconfigparser" 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/bootloader" 33 "github.com/snapcore/snapd/bootloader/assets" 34 "github.com/snapcore/snapd/bootloader/grubenv" 35 "github.com/snapcore/snapd/dirs" 36 "github.com/snapcore/snapd/osutil" 37 "github.com/snapcore/snapd/snap" 38 "github.com/snapcore/snapd/snap/snapfile" 39 "github.com/snapcore/snapd/snap/snaptest" 40 "github.com/snapcore/snapd/testutil" 41 ) 42 43 type grubTestSuite struct { 44 baseBootenvTestSuite 45 46 bootdir string 47 } 48 49 var _ = Suite(&grubTestSuite{}) 50 51 func (s *grubTestSuite) SetUpTest(c *C) { 52 s.baseBootenvTestSuite.SetUpTest(c) 53 bootloader.MockGrubFiles(c, s.rootdir) 54 55 s.bootdir = filepath.Join(s.rootdir, "boot") 56 } 57 58 // grubEditenvCmd finds the right grub{,2}-editenv command 59 func grubEditenvCmd() string { 60 for _, exe := range []string{"grub2-editenv", "grub-editenv"} { 61 if osutil.ExecutableExists(exe) { 62 return exe 63 } 64 } 65 return "" 66 } 67 68 func grubEnvPath(rootdir string) string { 69 return filepath.Join(rootdir, "boot/grub/grubenv") 70 } 71 72 func (s *grubTestSuite) grubEditenvSet(c *C, key, value string) { 73 if grubEditenvCmd() == "" { 74 c.Skip("grub{,2}-editenv is not available") 75 } 76 77 output, err := exec.Command(grubEditenvCmd(), grubEnvPath(s.rootdir), "set", fmt.Sprintf("%s=%s", key, value)).CombinedOutput() 78 c.Check(err, IsNil) 79 c.Check(string(output), Equals, "") 80 } 81 82 func (s *grubTestSuite) grubEditenvGet(c *C, key string) string { 83 if grubEditenvCmd() == "" { 84 c.Skip("grub{,2}-editenv is not available") 85 } 86 87 output, err := exec.Command(grubEditenvCmd(), grubEnvPath(s.rootdir), "list").CombinedOutput() 88 c.Assert(err, IsNil) 89 cfg := goconfigparser.New() 90 cfg.AllowNoSectionHeader = true 91 err = cfg.ReadString(string(output)) 92 c.Assert(err, IsNil) 93 v, err := cfg.Get("", key) 94 c.Assert(err, IsNil) 95 return v 96 } 97 98 func (s *grubTestSuite) makeFakeGrubEnv(c *C) { 99 s.grubEditenvSet(c, "k", "v") 100 } 101 102 func (s *grubTestSuite) TestNewGrub(c *C) { 103 s.makeFakeGrubEnv(c) 104 105 g := bootloader.NewGrub(s.rootdir, nil) 106 c.Assert(g, NotNil) 107 c.Assert(g.Name(), Equals, "grub") 108 } 109 110 func (s *grubTestSuite) TestGetBootloaderWithGrub(c *C) { 111 s.makeFakeGrubEnv(c) 112 113 bootloader, err := bootloader.Find(s.rootdir, nil) 114 c.Assert(err, IsNil) 115 c.Assert(bootloader.Name(), Equals, "grub") 116 } 117 118 func (s *grubTestSuite) TestGetBootloaderWithGrubWithDefaultRoot(c *C) { 119 s.makeFakeGrubEnv(c) 120 121 dirs.SetRootDir(s.rootdir) 122 defer func() { dirs.SetRootDir("") }() 123 124 bootloader, err := bootloader.Find("", nil) 125 c.Assert(err, IsNil) 126 c.Assert(bootloader.Name(), Equals, "grub") 127 } 128 129 func (s *grubTestSuite) TestGetBootVer(c *C) { 130 s.makeFakeGrubEnv(c) 131 s.grubEditenvSet(c, "snap_mode", "regular") 132 133 g := bootloader.NewGrub(s.rootdir, nil) 134 v, err := g.GetBootVars("snap_mode") 135 c.Assert(err, IsNil) 136 c.Check(v, HasLen, 1) 137 c.Check(v["snap_mode"], Equals, "regular") 138 } 139 140 func (s *grubTestSuite) TestSetBootVer(c *C) { 141 s.makeFakeGrubEnv(c) 142 143 g := bootloader.NewGrub(s.rootdir, nil) 144 err := g.SetBootVars(map[string]string{ 145 "k1": "v1", 146 "k2": "v2", 147 }) 148 c.Assert(err, IsNil) 149 150 c.Check(s.grubEditenvGet(c, "k1"), Equals, "v1") 151 c.Check(s.grubEditenvGet(c, "k2"), Equals, "v2") 152 } 153 154 func (s *grubTestSuite) TestExtractKernelAssetsNoUnpacksKernelForGrub(c *C) { 155 s.makeFakeGrubEnv(c) 156 157 g := bootloader.NewGrub(s.rootdir, nil) 158 159 files := [][]string{ 160 {"kernel.img", "I'm a kernel"}, 161 {"initrd.img", "...and I'm an initrd"}, 162 {"meta/kernel.yaml", "version: 4.2"}, 163 } 164 si := &snap.SideInfo{ 165 RealName: "ubuntu-kernel", 166 Revision: snap.R(42), 167 } 168 fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 169 snapf, err := snapfile.Open(fn) 170 c.Assert(err, IsNil) 171 172 info, err := snap.ReadInfoFromSnapFile(snapf, si) 173 c.Assert(err, IsNil) 174 175 err = g.ExtractKernelAssets(info, snapf) 176 c.Assert(err, IsNil) 177 178 // kernel is *not* here 179 kernimg := filepath.Join(s.bootdir, "grub", "ubuntu-kernel_42.snap", "kernel.img") 180 c.Assert(osutil.FileExists(kernimg), Equals, false) 181 } 182 183 func (s *grubTestSuite) TestExtractKernelForceWorks(c *C) { 184 s.makeFakeGrubEnv(c) 185 186 g := bootloader.NewGrub(s.rootdir, nil) 187 c.Assert(g, NotNil) 188 189 files := [][]string{ 190 {"kernel.img", "I'm a kernel"}, 191 {"initrd.img", "...and I'm an initrd"}, 192 {"meta/force-kernel-extraction", ""}, 193 {"meta/kernel.yaml", "version: 4.2"}, 194 } 195 si := &snap.SideInfo{ 196 RealName: "ubuntu-kernel", 197 Revision: snap.R(42), 198 } 199 fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 200 snapf, err := snapfile.Open(fn) 201 c.Assert(err, IsNil) 202 203 info, err := snap.ReadInfoFromSnapFile(snapf, si) 204 c.Assert(err, IsNil) 205 206 err = g.ExtractKernelAssets(info, snapf) 207 c.Assert(err, IsNil) 208 209 // kernel is extracted 210 kernimg := filepath.Join(s.bootdir, "grub", "ubuntu-kernel_42.snap", "kernel.img") 211 c.Assert(osutil.FileExists(kernimg), Equals, true) 212 // initrd 213 initrdimg := filepath.Join(s.bootdir, "grub", "ubuntu-kernel_42.snap", "initrd.img") 214 c.Assert(osutil.FileExists(initrdimg), Equals, true) 215 216 // ensure that removal of assets also works 217 err = g.RemoveKernelAssets(info) 218 c.Assert(err, IsNil) 219 exists, _, err := osutil.DirExists(filepath.Dir(kernimg)) 220 c.Assert(err, IsNil) 221 c.Check(exists, Equals, false) 222 } 223 224 func (s *grubTestSuite) grubDir() string { 225 return filepath.Join(s.bootdir, "grub") 226 } 227 228 func (s *grubTestSuite) grubEFINativeDir() string { 229 return filepath.Join(s.rootdir, "EFI/ubuntu") 230 } 231 232 func (s *grubTestSuite) makeFakeGrubEFINativeEnv(c *C, content []byte) { 233 err := os.MkdirAll(s.grubEFINativeDir(), 0755) 234 c.Assert(err, IsNil) 235 err = ioutil.WriteFile(filepath.Join(s.grubEFINativeDir(), "grub.cfg"), content, 0644) 236 c.Assert(err, IsNil) 237 } 238 239 func (s *grubTestSuite) TestNewGrubWithOptionRecovery(c *C) { 240 s.makeFakeGrubEFINativeEnv(c, nil) 241 242 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRecovery}) 243 c.Assert(g, NotNil) 244 c.Assert(g.Name(), Equals, "grub") 245 } 246 247 func (s *grubTestSuite) TestNewGrubWithOptionRecoveryBootEnv(c *C) { 248 s.makeFakeGrubEFINativeEnv(c, nil) 249 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRecovery}) 250 251 // check that setting vars goes to the right place 252 c.Check(filepath.Join(s.grubEFINativeDir(), "grubenv"), testutil.FileAbsent) 253 err := g.SetBootVars(map[string]string{ 254 "k1": "v1", 255 "k2": "v2", 256 }) 257 c.Assert(err, IsNil) 258 c.Check(filepath.Join(s.grubEFINativeDir(), "grubenv"), testutil.FilePresent) 259 260 env, err := g.GetBootVars("k1", "k2") 261 c.Assert(err, IsNil) 262 c.Check(env, DeepEquals, map[string]string{ 263 "k1": "v1", 264 "k2": "v2", 265 }) 266 } 267 268 func (s *grubTestSuite) TestNewGrubWithOptionRecoveryNoEnv(c *C) { 269 // fake a *regular* grub env 270 s.makeFakeGrubEnv(c) 271 272 // we can't create a recovery grub with that 273 g, err := bootloader.Find(s.rootdir, &bootloader.Options{Role: bootloader.RoleRecovery}) 274 c.Assert(g, IsNil) 275 c.Assert(err, Equals, bootloader.ErrBootloader) 276 } 277 278 func (s *grubTestSuite) TestGrubSetRecoverySystemEnv(c *C) { 279 s.makeFakeGrubEFINativeEnv(c, nil) 280 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRecovery}) 281 282 // check that we can set a recovery system specific bootenv 283 bvars := map[string]string{ 284 "snapd_recovery_kernel": "/snaps/pc-kernel_1.snap", 285 "other_options": "are-supported", 286 } 287 288 err := g.SetRecoverySystemEnv("/systems/20191209", bvars) 289 c.Assert(err, IsNil) 290 recoverySystemGrubenv := filepath.Join(s.rootdir, "/systems/20191209/grubenv") 291 c.Assert(recoverySystemGrubenv, testutil.FilePresent) 292 293 genv := grubenv.NewEnv(recoverySystemGrubenv) 294 err = genv.Load() 295 c.Assert(err, IsNil) 296 c.Check(genv.Get("snapd_recovery_kernel"), Equals, "/snaps/pc-kernel_1.snap") 297 c.Check(genv.Get("other_options"), Equals, "are-supported") 298 } 299 300 func (s *grubTestSuite) TestGetRecoverySystemEnv(c *C) { 301 s.makeFakeGrubEFINativeEnv(c, nil) 302 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRecovery}) 303 304 err := os.MkdirAll(filepath.Join(s.rootdir, "/systems/20191209"), 0755) 305 c.Assert(err, IsNil) 306 recoverySystemGrubenv := filepath.Join(s.rootdir, "/systems/20191209/grubenv") 307 308 // does not fail when there is no recovery env 309 value, err := g.GetRecoverySystemEnv("/systems/20191209", "no_file") 310 c.Assert(err, IsNil) 311 c.Check(value, Equals, "") 312 313 genv := grubenv.NewEnv(recoverySystemGrubenv) 314 genv.Set("snapd_extra_cmdline_args", "foo bar baz") 315 genv.Set("random_option", `has "some spaces"`) 316 err = genv.Save() 317 c.Assert(err, IsNil) 318 319 value, err = g.GetRecoverySystemEnv("/systems/20191209", "snapd_extra_cmdline_args") 320 c.Assert(err, IsNil) 321 c.Check(value, Equals, "foo bar baz") 322 value, err = g.GetRecoverySystemEnv("/systems/20191209", "random_option") 323 c.Assert(err, IsNil) 324 c.Check(value, Equals, `has "some spaces"`) 325 value, err = g.GetRecoverySystemEnv("/systems/20191209", "not_set") 326 c.Assert(err, IsNil) 327 c.Check(value, Equals, ``) 328 } 329 330 func (s *grubTestSuite) makeKernelAssetSnap(c *C, snapFileName string) snap.PlaceInfo { 331 kernelSnap, err := snap.ParsePlaceInfoFromSnapFileName(snapFileName) 332 c.Assert(err, IsNil) 333 334 // make a kernel.efi snap as it would be by ExtractKernelAssets() 335 kernelSnapExtractedAssetsDir := filepath.Join(s.grubDir(), snapFileName) 336 err = os.MkdirAll(kernelSnapExtractedAssetsDir, 0755) 337 c.Assert(err, IsNil) 338 339 err = ioutil.WriteFile(filepath.Join(kernelSnapExtractedAssetsDir, "kernel.efi"), nil, 0644) 340 c.Assert(err, IsNil) 341 342 return kernelSnap 343 } 344 345 func (s *grubTestSuite) makeKernelAssetSnapAndSymlink(c *C, snapFileName, symlinkName string) snap.PlaceInfo { 346 kernelSnap := s.makeKernelAssetSnap(c, snapFileName) 347 348 // make a kernel.efi symlink to the kernel.efi above 349 err := os.Symlink( 350 filepath.Join(snapFileName, "kernel.efi"), 351 filepath.Join(s.grubDir(), symlinkName), 352 ) 353 c.Assert(err, IsNil) 354 355 return kernelSnap 356 } 357 358 func (s *grubTestSuite) TestGrubExtractedRunKernelImageKernel(c *C) { 359 s.makeFakeGrubEnv(c) 360 g := bootloader.NewGrub(s.rootdir, nil) 361 eg, ok := g.(bootloader.ExtractedRunKernelImageBootloader) 362 c.Assert(ok, Equals, true) 363 364 kernel := s.makeKernelAssetSnapAndSymlink(c, "pc-kernel_1.snap", "kernel.efi") 365 366 // ensure that the returned kernel is the same as the one we put there 367 sn, err := eg.Kernel() 368 c.Assert(err, IsNil) 369 c.Assert(sn, DeepEquals, kernel) 370 } 371 372 func (s *grubTestSuite) TestGrubExtractedRunKernelImageTryKernel(c *C) { 373 s.makeFakeGrubEnv(c) 374 g := bootloader.NewGrub(s.rootdir, nil) 375 eg, ok := g.(bootloader.ExtractedRunKernelImageBootloader) 376 c.Assert(ok, Equals, true) 377 378 // ensure it doesn't return anything when the symlink doesn't exist 379 _, err := eg.TryKernel() 380 c.Assert(err, Equals, bootloader.ErrNoTryKernelRef) 381 382 // when a bad kernel snap name is in the extracted path, it will complain 383 // appropriately 384 kernelSnapExtractedAssetsDir := filepath.Join(s.grubDir(), "bad_snap_rev_name") 385 badKernelSnapPath := filepath.Join(kernelSnapExtractedAssetsDir, "kernel.efi") 386 tryKernelSymlink := filepath.Join(s.grubDir(), "try-kernel.efi") 387 err = os.MkdirAll(kernelSnapExtractedAssetsDir, 0755) 388 c.Assert(err, IsNil) 389 390 err = ioutil.WriteFile(badKernelSnapPath, nil, 0644) 391 c.Assert(err, IsNil) 392 393 err = os.Symlink("bad_snap_rev_name/kernel.efi", tryKernelSymlink) 394 c.Assert(err, IsNil) 395 396 _, err = eg.TryKernel() 397 c.Assert(err, ErrorMatches, "cannot parse kernel snap file name from symlink target \"bad_snap_rev_name\": .*") 398 399 // remove the bad symlink 400 err = os.Remove(tryKernelSymlink) 401 c.Assert(err, IsNil) 402 403 // make a real symlink 404 tryKernel := s.makeKernelAssetSnapAndSymlink(c, "pc-kernel_2.snap", "try-kernel.efi") 405 406 // ensure that the returned kernel is the same as the one we put there 407 sn, err := eg.TryKernel() 408 c.Assert(err, IsNil) 409 c.Assert(sn, DeepEquals, tryKernel) 410 411 // if the destination of the symlink is removed, we get an error 412 err = os.Remove(filepath.Join(s.grubDir(), "pc-kernel_2.snap", "kernel.efi")) 413 c.Assert(err, IsNil) 414 _, err = eg.TryKernel() 415 c.Assert(err, ErrorMatches, "cannot read dangling symlink try-kernel.efi") 416 } 417 418 func (s *grubTestSuite) TestGrubExtractedRunKernelImageEnableKernel(c *C) { 419 s.makeFakeGrubEnv(c) 420 g := bootloader.NewGrub(s.rootdir, nil) 421 eg, ok := g.(bootloader.ExtractedRunKernelImageBootloader) 422 c.Assert(ok, Equals, true) 423 424 // ensure we fail to create a dangling symlink to a kernel snap that was not 425 // actually extracted 426 nonExistSnap, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_12.snap") 427 c.Assert(err, IsNil) 428 err = eg.EnableKernel(nonExistSnap) 429 c.Assert(err, ErrorMatches, "cannot enable kernel.efi at pc-kernel_12.snap/kernel.efi: file does not exist") 430 431 kernel := s.makeKernelAssetSnap(c, "pc-kernel_1.snap") 432 433 // enable the Kernel we extracted 434 err = eg.EnableKernel(kernel) 435 c.Assert(err, IsNil) 436 437 // ensure that the symlink was put where we expect it 438 asset, err := os.Readlink(filepath.Join(s.grubDir(), "kernel.efi")) 439 c.Assert(err, IsNil) 440 c.Assert(asset, DeepEquals, filepath.Join("pc-kernel_1.snap", "kernel.efi")) 441 442 // create a new kernel snap and ensure that we can safely enable that one 443 // too 444 kernel2 := s.makeKernelAssetSnap(c, "pc-kernel_2.snap") 445 err = eg.EnableKernel(kernel2) 446 c.Assert(err, IsNil) 447 448 // ensure that the symlink was put where we expect it 449 asset, err = os.Readlink(filepath.Join(s.grubDir(), "kernel.efi")) 450 c.Assert(err, IsNil) 451 c.Assert(asset, DeepEquals, filepath.Join("pc-kernel_2.snap", "kernel.efi")) 452 } 453 454 func (s *grubTestSuite) TestGrubExtractedRunKernelImageEnableTryKernel(c *C) { 455 s.makeFakeGrubEnv(c) 456 g := bootloader.NewGrub(s.rootdir, nil) 457 eg, ok := g.(bootloader.ExtractedRunKernelImageBootloader) 458 c.Assert(ok, Equals, true) 459 460 kernel := s.makeKernelAssetSnap(c, "pc-kernel_1.snap") 461 462 // enable the Kernel we extracted 463 err := eg.EnableTryKernel(kernel) 464 c.Assert(err, IsNil) 465 466 // ensure that the symlink was put where we expect it 467 asset, err := os.Readlink(filepath.Join(s.grubDir(), "try-kernel.efi")) 468 c.Assert(err, IsNil) 469 470 c.Assert(asset, DeepEquals, filepath.Join("pc-kernel_1.snap", "kernel.efi")) 471 } 472 473 func (s *grubTestSuite) TestGrubExtractedRunKernelImageDisableTryKernel(c *C) { 474 s.makeFakeGrubEnv(c) 475 g := bootloader.NewGrub(s.rootdir, nil) 476 eg, ok := g.(bootloader.ExtractedRunKernelImageBootloader) 477 c.Assert(ok, Equals, true) 478 479 // trying to disable when the try-kernel.efi symlink is missing does not 480 // raise any errors 481 err := eg.DisableTryKernel() 482 c.Assert(err, IsNil) 483 484 // make the symlink and check that the symlink is missing afterwards 485 s.makeKernelAssetSnapAndSymlink(c, "pc-kernel_1.snap", "try-kernel.efi") 486 // make sure symlink is there 487 c.Assert(filepath.Join(s.grubDir(), "try-kernel.efi"), testutil.FilePresent) 488 489 err = eg.DisableTryKernel() 490 c.Assert(err, IsNil) 491 492 // ensure that the symlink is no longer there 493 c.Assert(filepath.Join(s.grubDir(), "try-kernel.efi"), testutil.FileAbsent) 494 c.Assert(filepath.Join(s.grubDir(), "pc-kernel_1.snap/kernel.efi"), testutil.FilePresent) 495 496 // try again but make sure that the directory cannot be written to 497 s.makeKernelAssetSnapAndSymlink(c, "pc-kernel_1.snap", "try-kernel.efi") 498 err = os.Chmod(s.grubDir(), 000) 499 c.Assert(err, IsNil) 500 defer os.Chmod(s.grubDir(), 0755) 501 502 err = eg.DisableTryKernel() 503 c.Assert(err, ErrorMatches, "remove .*/grub/try-kernel.efi: permission denied") 504 } 505 506 func (s *grubTestSuite) TestKernelExtractionRunImageKernel(c *C) { 507 s.makeFakeGrubEnv(c) 508 509 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRunMode}) 510 c.Assert(g, NotNil) 511 512 files := [][]string{ 513 {"kernel.efi", "I'm a kernel"}, 514 {"another-kernel-file", "another kernel file"}, 515 {"meta/kernel.yaml", "version: 4.2"}, 516 } 517 si := &snap.SideInfo{ 518 RealName: "ubuntu-kernel", 519 Revision: snap.R(42), 520 } 521 fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 522 snapf, err := snapfile.Open(fn) 523 c.Assert(err, IsNil) 524 525 info, err := snap.ReadInfoFromSnapFile(snapf, si) 526 c.Assert(err, IsNil) 527 528 err = g.ExtractKernelAssets(info, snapf) 529 c.Assert(err, IsNil) 530 531 // kernel is extracted 532 kernefi := filepath.Join(s.bootdir, "grub", "ubuntu-kernel_42.snap", "kernel.efi") 533 c.Assert(kernefi, testutil.FilePresent) 534 // other file is not extracted 535 other := filepath.Join(s.bootdir, "grub", "ubuntu-kernel_42.snap", "another-kernel-file") 536 c.Assert(other, testutil.FileAbsent) 537 538 // ensure that removal of assets also works 539 err = g.RemoveKernelAssets(info) 540 c.Assert(err, IsNil) 541 exists, _, err := osutil.DirExists(filepath.Dir(kernefi)) 542 c.Assert(err, IsNil) 543 c.Check(exists, Equals, false) 544 } 545 546 func (s *grubTestSuite) TestKernelExtractionRunImageKernelNoSlashBoot(c *C) { 547 // this is ubuntu-boot but during install we use the native EFI/ubuntu 548 // layout, same as Recovery, without the /boot mount 549 s.makeFakeGrubEFINativeEnv(c, nil) 550 551 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRunMode, NoSlashBoot: true}) 552 c.Assert(g, NotNil) 553 554 files := [][]string{ 555 {"kernel.efi", "I'm a kernel"}, 556 {"another-kernel-file", "another kernel file"}, 557 {"meta/kernel.yaml", "version: 4.2"}, 558 } 559 si := &snap.SideInfo{ 560 RealName: "ubuntu-kernel", 561 Revision: snap.R(42), 562 } 563 fn := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 564 snapf, err := snapfile.Open(fn) 565 c.Assert(err, IsNil) 566 567 info, err := snap.ReadInfoFromSnapFile(snapf, si) 568 c.Assert(err, IsNil) 569 570 err = g.ExtractKernelAssets(info, snapf) 571 c.Assert(err, IsNil) 572 573 // kernel is extracted 574 kernefi := filepath.Join(s.rootdir, "EFI/ubuntu", "ubuntu-kernel_42.snap", "kernel.efi") 575 c.Assert(kernefi, testutil.FilePresent) 576 // other file is not extracted 577 other := filepath.Join(s.rootdir, "EFI/ubuntu", "ubuntu-kernel_42.snap", "another-kernel-file") 578 c.Assert(other, testutil.FileAbsent) 579 580 // enable the Kernel we extracted 581 eg, ok := g.(bootloader.ExtractedRunKernelImageBootloader) 582 c.Assert(ok, Equals, true) 583 err = eg.EnableKernel(info) 584 c.Assert(err, IsNil) 585 586 // ensure that the symlink was put where we expect it 587 asset, err := os.Readlink(filepath.Join(s.rootdir, "EFI/ubuntu", "kernel.efi")) 588 c.Assert(err, IsNil) 589 590 c.Assert(asset, DeepEquals, filepath.Join("ubuntu-kernel_42.snap", "kernel.efi")) 591 592 // ensure that removal of assets also works 593 err = g.RemoveKernelAssets(info) 594 c.Assert(err, IsNil) 595 exists, _, err := osutil.DirExists(filepath.Dir(kernefi)) 596 c.Assert(err, IsNil) 597 c.Check(exists, Equals, false) 598 } 599 600 func (s *grubTestSuite) TestListManagedAssets(c *C) { 601 s.makeFakeGrubEFINativeEnv(c, []byte(`this is 602 some random boot config`)) 603 604 opts := &bootloader.Options{NoSlashBoot: true} 605 g := bootloader.NewGrub(s.rootdir, opts) 606 c.Assert(g, NotNil) 607 608 tg, ok := g.(bootloader.TrustedAssetsBootloader) 609 c.Assert(ok, Equals, true) 610 611 c.Check(tg.ManagedAssets(), DeepEquals, []string{ 612 "EFI/ubuntu/grub.cfg", 613 }) 614 615 opts = &bootloader.Options{Role: bootloader.RoleRecovery} 616 tg = bootloader.NewGrub(s.rootdir, opts).(bootloader.TrustedAssetsBootloader) 617 c.Check(tg.ManagedAssets(), DeepEquals, []string{ 618 "EFI/ubuntu/grub.cfg", 619 }) 620 621 // as it called for the root fs rather than a mount point of a partition 622 // with boot assets 623 tg = bootloader.NewGrub(s.rootdir, nil).(bootloader.TrustedAssetsBootloader) 624 c.Check(tg.ManagedAssets(), DeepEquals, []string{ 625 "boot/grub/grub.cfg", 626 }) 627 } 628 629 func (s *grubTestSuite) TestRecoveryUpdateBootConfigNoEdition(c *C) { 630 // native EFI/ubuntu setup 631 s.makeFakeGrubEFINativeEnv(c, []byte("recovery boot script")) 632 633 opts := &bootloader.Options{Role: bootloader.RoleRecovery} 634 g := bootloader.NewGrub(s.rootdir, opts) 635 c.Assert(g, NotNil) 636 637 restore := assets.MockInternal("grub-recovery.cfg", []byte(`# Snapd-Boot-Config-Edition: 5 638 this is mocked grub-recovery.conf 639 `)) 640 defer restore() 641 642 tg, ok := g.(bootloader.TrustedAssetsBootloader) 643 c.Assert(ok, Equals, true) 644 // install the recovery boot script 645 err := tg.UpdateBootConfig(opts) 646 c.Assert(err, IsNil) 647 648 c.Assert(filepath.Join(s.grubEFINativeDir(), "grub.cfg"), testutil.FileEquals, `recovery boot script`) 649 } 650 651 func (s *grubTestSuite) TestRecoveryUpdateBootConfigUpdates(c *C) { 652 // native EFI/ubuntu setup 653 s.makeFakeGrubEFINativeEnv(c, []byte(`# Snapd-Boot-Config-Edition: 1 654 recovery boot script`)) 655 656 opts := &bootloader.Options{Role: bootloader.RoleRecovery} 657 g := bootloader.NewGrub(s.rootdir, opts) 658 c.Assert(g, NotNil) 659 660 restore := assets.MockInternal("grub-recovery.cfg", []byte(`# Snapd-Boot-Config-Edition: 3 661 this is mocked grub-recovery.conf 662 `)) 663 defer restore() 664 restore = assets.MockInternal("grub.cfg", []byte(`# Snapd-Boot-Config-Edition: 4 665 this is mocked grub.conf 666 `)) 667 defer restore() 668 tg, ok := g.(bootloader.TrustedAssetsBootloader) 669 c.Assert(ok, Equals, true) 670 // install the recovery boot script 671 err := tg.UpdateBootConfig(opts) 672 c.Assert(err, IsNil) 673 // the recovery boot asset was picked 674 c.Assert(filepath.Join(s.grubEFINativeDir(), "grub.cfg"), testutil.FileEquals, `# Snapd-Boot-Config-Edition: 3 675 this is mocked grub-recovery.conf 676 `) 677 } 678 679 func (s *grubTestSuite) testBootUpdateBootConfigUpdates(c *C, oldConfig, newConfig string, update bool) { 680 // native EFI/ubuntu setup 681 s.makeFakeGrubEFINativeEnv(c, []byte(oldConfig)) 682 683 opts := &bootloader.Options{NoSlashBoot: true} 684 g := bootloader.NewGrub(s.rootdir, opts) 685 c.Assert(g, NotNil) 686 687 restore := assets.MockInternal("grub.cfg", []byte(newConfig)) 688 defer restore() 689 690 tg, ok := g.(bootloader.TrustedAssetsBootloader) 691 c.Assert(ok, Equals, true) 692 err := tg.UpdateBootConfig(opts) 693 c.Assert(err, IsNil) 694 if update { 695 c.Assert(filepath.Join(s.grubEFINativeDir(), "grub.cfg"), testutil.FileEquals, newConfig) 696 } else { 697 c.Assert(filepath.Join(s.grubEFINativeDir(), "grub.cfg"), testutil.FileEquals, oldConfig) 698 } 699 } 700 701 func (s *grubTestSuite) TestNoSlashBootUpdateBootConfigNoUpdateWhenNotManaged(c *C) { 702 oldConfig := `not managed` 703 newConfig := `# Snapd-Boot-Config-Edition: 3 704 this update is not applied 705 ` 706 // the current boot config is not managed, no update applied 707 const updateApplied = false 708 s.testBootUpdateBootConfigUpdates(c, oldConfig, newConfig, updateApplied) 709 } 710 711 func (s *grubTestSuite) TestNoSlashBootUpdateBootConfigUpdates(c *C) { 712 oldConfig := `# Snapd-Boot-Config-Edition: 2 713 boot script 714 ` 715 // edition is higher, update is applied 716 newConfig := `# Snapd-Boot-Config-Edition: 3 717 this is updated grub.cfg 718 ` 719 const updateApplied = true 720 s.testBootUpdateBootConfigUpdates(c, oldConfig, newConfig, updateApplied) 721 } 722 723 func (s *grubTestSuite) TestNoSlashBootUpdateBootConfigNoUpdate(c *C) { 724 oldConfig := `# Snapd-Boot-Config-Edition: 2 725 boot script 726 ` 727 // edition is lower, no update is applied 728 newConfig := `# Snapd-Boot-Config-Edition: 1 729 this is updated grub.cfg 730 ` 731 const updateApplied = false 732 s.testBootUpdateBootConfigUpdates(c, oldConfig, newConfig, updateApplied) 733 } 734 735 func (s *grubTestSuite) TestNoSlashBootUpdateBootConfigSameEdition(c *C) { 736 oldConfig := `# Snapd-Boot-Config-Edition: 1 737 boot script 738 ` 739 // edition is equal, no update is applied 740 newConfig := `# Snapd-Boot-Config-Edition: 1 741 this is updated grub.cfg 742 ` 743 const updateApplied = false 744 s.testBootUpdateBootConfigUpdates(c, oldConfig, newConfig, updateApplied) 745 } 746 747 func (s *grubTestSuite) TestBootUpdateBootConfigTrivialErr(c *C) { 748 oldConfig := `# Snapd-Boot-Config-Edition: 2 749 boot script 750 ` 751 // edition is higher, update is applied 752 newConfig := `# Snapd-Boot-Config-Edition: 3 753 this is updated grub.cfg 754 ` 755 // native EFI/ubuntu setup 756 s.makeFakeGrubEFINativeEnv(c, []byte(oldConfig)) 757 restore := assets.MockInternal("grub.cfg", []byte(newConfig)) 758 defer restore() 759 760 opts := &bootloader.Options{NoSlashBoot: true} 761 g := bootloader.NewGrub(s.rootdir, opts) 762 c.Assert(g, NotNil) 763 tg, ok := g.(bootloader.TrustedAssetsBootloader) 764 c.Assert(ok, Equals, true) 765 766 err := os.Chmod(s.grubEFINativeDir(), 0000) 767 c.Assert(err, IsNil) 768 defer os.Chmod(s.grubEFINativeDir(), 0755) 769 770 err = tg.UpdateBootConfig(opts) 771 c.Assert(err, ErrorMatches, "cannot load existing config asset: .*/EFI/ubuntu/grub.cfg: permission denied") 772 err = os.Chmod(s.grubEFINativeDir(), 0555) 773 c.Assert(err, IsNil) 774 775 c.Assert(filepath.Join(s.grubEFINativeDir(), "grub.cfg"), testutil.FileEquals, oldConfig) 776 777 // writing out new config fails 778 err = os.Chmod(s.grubEFINativeDir(), 0111) 779 c.Assert(err, IsNil) 780 err = tg.UpdateBootConfig(opts) 781 c.Assert(err, ErrorMatches, `open .*/EFI/ubuntu/grub.cfg\..+: permission denied`) 782 c.Assert(filepath.Join(s.grubEFINativeDir(), "grub.cfg"), testutil.FileEquals, oldConfig) 783 } 784 785 func (s *grubTestSuite) TestStaticCmdlineForGrubAsset(c *C) { 786 restore := assets.MockSnippetsForEdition("grub-asset:static-cmdline", []assets.ForEditions{ 787 {FirstEdition: 2, Snippet: []byte(`static cmdline "with spaces"`)}, 788 }) 789 defer restore() 790 cmdline := bootloader.StaticCommandLineForGrubAssetEdition("grub-asset", 1) 791 c.Check(cmdline, Equals, ``) 792 cmdline = bootloader.StaticCommandLineForGrubAssetEdition("grub-asset", 2) 793 c.Check(cmdline, Equals, `static cmdline "with spaces"`) 794 cmdline = bootloader.StaticCommandLineForGrubAssetEdition("grub-asset", 4) 795 c.Check(cmdline, Equals, `static cmdline "with spaces"`) 796 } 797 798 func (s *grubTestSuite) TestCommandLineNotManaged(c *C) { 799 grubCfg := "boot script\n" 800 801 // native EFI/ubuntu setup 802 s.makeFakeGrubEFINativeEnv(c, []byte(grubCfg)) 803 804 restore := assets.MockSnippetsForEdition("grub.cfg:static-cmdline", []assets.ForEditions{ 805 {FirstEdition: 1, Snippet: []byte(`static=1`)}, 806 {FirstEdition: 2, Snippet: []byte(`static=2`)}, 807 }) 808 defer restore() 809 restore = assets.MockSnippetsForEdition("grub-recovery.cfg:static-cmdline", []assets.ForEditions{ 810 {FirstEdition: 1, Snippet: []byte(`static=1 recovery`)}, 811 {FirstEdition: 2, Snippet: []byte(`static=2 recovery`)}, 812 }) 813 defer restore() 814 815 opts := &bootloader.Options{NoSlashBoot: true} 816 mg := bootloader.NewGrub(s.rootdir, opts).(bootloader.TrustedAssetsBootloader) 817 818 args, err := mg.CommandLine("snapd_recovery_mode=run", "", "extra") 819 c.Assert(err, IsNil) 820 c.Check(args, Equals, "snapd_recovery_mode=run static=1 extra") 821 822 optsRecovery := &bootloader.Options{NoSlashBoot: true, Role: bootloader.RoleRecovery} 823 mgr := bootloader.NewGrub(s.rootdir, optsRecovery).(bootloader.TrustedAssetsBootloader) 824 825 args, err = mgr.CommandLine("snapd_recovery_mode=recover", "snapd_recovery_system=1234", "extra") 826 c.Assert(err, IsNil) 827 c.Check(args, Equals, "snapd_recovery_mode=recover snapd_recovery_system=1234 static=1 recovery extra") 828 } 829 830 func (s *grubTestSuite) TestCommandLineMocked(c *C) { 831 grubCfg := `# Snapd-Boot-Config-Edition: 2 832 boot script 833 ` 834 staticCmdline := `arg1 foo=123 panic=-1 arg2="with spaces "` 835 staticCmdlineEdition3 := `edition=3 static args` 836 restore := assets.MockSnippetsForEdition("grub.cfg:static-cmdline", []assets.ForEditions{ 837 {FirstEdition: 1, Snippet: []byte(staticCmdline)}, 838 {FirstEdition: 3, Snippet: []byte(staticCmdlineEdition3)}, 839 }) 840 defer restore() 841 staticCmdlineRecovery := `recovery config panic=-1` 842 restore = assets.MockSnippetsForEdition("grub-recovery.cfg:static-cmdline", []assets.ForEditions{ 843 {FirstEdition: 1, Snippet: []byte(staticCmdlineRecovery)}, 844 }) 845 defer restore() 846 847 // native EFI/ubuntu setup 848 s.makeFakeGrubEFINativeEnv(c, []byte(grubCfg)) 849 850 optsNoSlashBoot := &bootloader.Options{NoSlashBoot: true} 851 g := bootloader.NewGrub(s.rootdir, optsNoSlashBoot) 852 c.Assert(g, NotNil) 853 tg, ok := g.(bootloader.TrustedAssetsBootloader) 854 c.Assert(ok, Equals, true) 855 856 extraArgs := `extra_arg=1 extra_foo=-1 panic=3 baz="more spaces"` 857 args, err := tg.CommandLine("snapd_recovery_mode=run", "", extraArgs) 858 c.Assert(err, IsNil) 859 c.Check(args, Equals, `snapd_recovery_mode=run arg1 foo=123 panic=-1 arg2="with spaces " extra_arg=1 extra_foo=-1 panic=3 baz="more spaces"`) 860 861 // empty mode/system do not produce confusing results 862 args, err = tg.CommandLine("", "", extraArgs) 863 c.Assert(err, IsNil) 864 c.Check(args, Equals, `arg1 foo=123 panic=-1 arg2="with spaces " extra_arg=1 extra_foo=-1 panic=3 baz="more spaces"`) 865 866 // now check the recovery bootloader 867 optsRecovery := &bootloader.Options{NoSlashBoot: true, Role: bootloader.RoleRecovery} 868 mrg := bootloader.NewGrub(s.rootdir, optsRecovery).(bootloader.TrustedAssetsBootloader) 869 args, err = mrg.CommandLine("snapd_recovery_mode=recover", "snapd_recovery_system=20200202", extraArgs) 870 c.Assert(err, IsNil) 871 // static command line from recovery asset 872 c.Check(args, Equals, `snapd_recovery_mode=recover snapd_recovery_system=20200202 recovery config panic=-1 extra_arg=1 extra_foo=-1 panic=3 baz="more spaces"`) 873 874 // try with a different edition 875 grubCfg3 := `# Snapd-Boot-Config-Edition: 3 876 boot script 877 ` 878 s.makeFakeGrubEFINativeEnv(c, []byte(grubCfg3)) 879 tg = bootloader.NewGrub(s.rootdir, optsNoSlashBoot).(bootloader.TrustedAssetsBootloader) 880 c.Assert(g, NotNil) 881 extraArgs = `extra_arg=1` 882 args, err = tg.CommandLine("snapd_recovery_mode=run", "", extraArgs) 883 c.Assert(err, IsNil) 884 c.Check(args, Equals, `snapd_recovery_mode=run edition=3 static args extra_arg=1`) 885 } 886 887 func (s *grubTestSuite) TestCandidateCommandLineMocked(c *C) { 888 grubCfg := `# Snapd-Boot-Config-Edition: 1 889 boot script 890 ` 891 // edition on disk 892 s.makeFakeGrubEFINativeEnv(c, []byte(grubCfg)) 893 894 edition2 := []byte(`# Snapd-Boot-Config-Edition: 2`) 895 edition3 := []byte(`# Snapd-Boot-Config-Edition: 3`) 896 edition4 := []byte(`# Snapd-Boot-Config-Edition: 4`) 897 898 restore := assets.MockInternal("grub.cfg", edition2) 899 defer restore() 900 restore = assets.MockInternal("grub-recovery.cfg", edition2) 901 defer restore() 902 903 restore = assets.MockSnippetsForEdition("grub.cfg:static-cmdline", []assets.ForEditions{ 904 {FirstEdition: 1, Snippet: []byte(`edition=1`)}, 905 {FirstEdition: 3, Snippet: []byte(`edition=3`)}, 906 }) 907 defer restore() 908 restore = assets.MockSnippetsForEdition("grub-recovery.cfg:static-cmdline", []assets.ForEditions{ 909 {FirstEdition: 1, Snippet: []byte(`recovery edition=1`)}, 910 {FirstEdition: 3, Snippet: []byte(`recovery edition=3`)}, 911 {FirstEdition: 4, Snippet: []byte(`recovery edition=4up`)}, 912 }) 913 defer restore() 914 915 optsNoSlashBoot := &bootloader.Options{NoSlashBoot: true} 916 mg := bootloader.NewGrub(s.rootdir, optsNoSlashBoot).(bootloader.TrustedAssetsBootloader) 917 optsRecovery := &bootloader.Options{NoSlashBoot: true, Role: bootloader.RoleRecovery} 918 recoverymg := bootloader.NewGrub(s.rootdir, optsRecovery).(bootloader.TrustedAssetsBootloader) 919 920 args, err := mg.CandidateCommandLine("snapd_recovery_mode=run", "", "extra=1") 921 c.Assert(err, IsNil) 922 c.Check(args, Equals, `snapd_recovery_mode=run edition=1 extra=1`) 923 args, err = recoverymg.CandidateCommandLine("snapd_recovery_mode=recover", "snapd_recovery_system=20200202", "extra=1") 924 c.Assert(err, IsNil) 925 c.Check(args, Equals, `snapd_recovery_mode=recover snapd_recovery_system=20200202 recovery edition=1 extra=1`) 926 927 restore = assets.MockInternal("grub.cfg", edition3) 928 defer restore() 929 restore = assets.MockInternal("grub-recovery.cfg", edition3) 930 defer restore() 931 932 args, err = mg.CandidateCommandLine("snapd_recovery_mode=run", "", "extra=1") 933 c.Assert(err, IsNil) 934 c.Check(args, Equals, `snapd_recovery_mode=run edition=3 extra=1`) 935 args, err = recoverymg.CandidateCommandLine("snapd_recovery_mode=recover", "snapd_recovery_system=20200202", "extra=1") 936 c.Assert(err, IsNil) 937 c.Check(args, Equals, `snapd_recovery_mode=recover snapd_recovery_system=20200202 recovery edition=3 extra=1`) 938 939 // bump edition only for recovery 940 restore = assets.MockInternal("grub-recovery.cfg", edition4) 941 defer restore() 942 // boot bootloader unchanged 943 args, err = mg.CandidateCommandLine("snapd_recovery_mode=run", "", "extra=1") 944 c.Assert(err, IsNil) 945 c.Check(args, Equals, `snapd_recovery_mode=run edition=3 extra=1`) 946 // recovery uses a new edition 947 args, err = recoverymg.CandidateCommandLine("snapd_recovery_mode=recover", "snapd_recovery_system=20200202", "extra=1") 948 c.Assert(err, IsNil) 949 c.Check(args, Equals, `snapd_recovery_mode=recover snapd_recovery_system=20200202 recovery edition=4up extra=1`) 950 } 951 952 func (s *grubTestSuite) TestCommandLineReal(c *C) { 953 grubCfg := `# Snapd-Boot-Config-Edition: 1 954 boot script 955 ` 956 // native EFI/ubuntu setup 957 s.makeFakeGrubEFINativeEnv(c, []byte(grubCfg)) 958 959 opts := &bootloader.Options{NoSlashBoot: true} 960 g := bootloader.NewGrub(s.rootdir, opts) 961 c.Assert(g, NotNil) 962 tg, ok := g.(bootloader.TrustedAssetsBootloader) 963 c.Assert(ok, Equals, true) 964 965 extraArgs := "foo bar baz=1" 966 args, err := tg.CommandLine("snapd_recovery_mode=run", "", extraArgs) 967 c.Assert(err, IsNil) 968 c.Check(args, Equals, `snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1 foo bar baz=1`) 969 970 // now check the recovery bootloader 971 opts = &bootloader.Options{NoSlashBoot: true, Role: bootloader.RoleRecovery} 972 mrg := bootloader.NewGrub(s.rootdir, opts).(bootloader.TrustedAssetsBootloader) 973 args, err = mrg.CommandLine("snapd_recovery_mode=recover", "snapd_recovery_system=20200202", extraArgs) 974 c.Assert(err, IsNil) 975 // static command line from recovery asset 976 c.Check(args, Equals, `snapd_recovery_mode=recover snapd_recovery_system=20200202 console=ttyS0 console=tty1 panic=-1 foo bar baz=1`) 977 } 978 979 func (s *grubTestSuite) TestTrustedAssetsNativePartitionLayout(c *C) { 980 // native EFI/ubuntu setup 981 s.makeFakeGrubEFINativeEnv(c, []byte("grub.cfg")) 982 opts := &bootloader.Options{NoSlashBoot: true} 983 g := bootloader.NewGrub(s.rootdir, opts) 984 c.Assert(g, NotNil) 985 986 tab, ok := g.(bootloader.TrustedAssetsBootloader) 987 c.Assert(ok, Equals, true) 988 989 ta, err := tab.TrustedAssets() 990 c.Assert(err, IsNil) 991 c.Check(ta, DeepEquals, []string{ 992 "EFI/boot/grubx64.efi", 993 }) 994 995 // recovery bootloader 996 recoveryOpts := &bootloader.Options{NoSlashBoot: true, Role: bootloader.RoleRecovery} 997 tarb := bootloader.NewGrub(s.rootdir, recoveryOpts).(bootloader.TrustedAssetsBootloader) 998 c.Assert(tarb, NotNil) 999 1000 ta, err = tarb.TrustedAssets() 1001 c.Assert(err, IsNil) 1002 c.Check(ta, DeepEquals, []string{ 1003 "EFI/boot/bootx64.efi", 1004 "EFI/boot/grubx64.efi", 1005 }) 1006 1007 } 1008 1009 func (s *grubTestSuite) TestTrustedAssetsRoot(c *C) { 1010 s.makeFakeGrubEnv(c) 1011 g := bootloader.NewGrub(s.rootdir, nil) 1012 tab, ok := g.(bootloader.TrustedAssetsBootloader) 1013 c.Assert(ok, Equals, true) 1014 1015 ta, err := tab.TrustedAssets() 1016 c.Assert(err, ErrorMatches, "internal error: trusted assets called without native host-partition layout") 1017 c.Check(ta, IsNil) 1018 } 1019 1020 func (s *grubTestSuite) TestRecoveryBootChains(c *C) { 1021 s.makeFakeGrubEFINativeEnv(c, nil) 1022 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRecovery}) 1023 tab, ok := g.(bootloader.TrustedAssetsBootloader) 1024 c.Assert(ok, Equals, true) 1025 1026 chain, err := tab.RecoveryBootChain("kernel.snap") 1027 c.Assert(err, IsNil) 1028 c.Assert(chain, DeepEquals, []bootloader.BootFile{ 1029 {Path: "EFI/boot/bootx64.efi", Role: bootloader.RoleRecovery}, 1030 {Path: "EFI/boot/grubx64.efi", Role: bootloader.RoleRecovery}, 1031 {Snap: "kernel.snap", Path: "kernel.efi", Role: bootloader.RoleRecovery}, 1032 }) 1033 } 1034 1035 func (s *grubTestSuite) TestRecoveryBootChainsNotRecoveryBootloader(c *C) { 1036 s.makeFakeGrubEnv(c) 1037 g := bootloader.NewGrub(s.rootdir, nil) 1038 tab, ok := g.(bootloader.TrustedAssetsBootloader) 1039 c.Assert(ok, Equals, true) 1040 1041 _, err := tab.RecoveryBootChain("kernel.snap") 1042 c.Assert(err, ErrorMatches, "not a recovery bootloader") 1043 } 1044 1045 func (s *grubTestSuite) TestBootChains(c *C) { 1046 s.makeFakeGrubEFINativeEnv(c, nil) 1047 g := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRecovery}) 1048 tab, ok := g.(bootloader.TrustedAssetsBootloader) 1049 c.Assert(ok, Equals, true) 1050 1051 g2 := bootloader.NewGrub(s.rootdir, &bootloader.Options{Role: bootloader.RoleRunMode}) 1052 1053 chain, err := tab.BootChain(g2, "kernel.snap") 1054 c.Assert(err, IsNil) 1055 c.Assert(chain, DeepEquals, []bootloader.BootFile{ 1056 {Path: "EFI/boot/bootx64.efi", Role: bootloader.RoleRecovery}, 1057 {Path: "EFI/boot/grubx64.efi", Role: bootloader.RoleRecovery}, 1058 {Path: "EFI/boot/grubx64.efi", Role: bootloader.RoleRunMode}, 1059 {Snap: "kernel.snap", Path: "kernel.efi", Role: bootloader.RoleRunMode}, 1060 }) 1061 } 1062 1063 func (s *grubTestSuite) TestBootChainsNotRecoveryBootloader(c *C) { 1064 s.makeFakeGrubEnv(c) 1065 g := bootloader.NewGrub(s.rootdir, nil) 1066 tab, ok := g.(bootloader.TrustedAssetsBootloader) 1067 c.Assert(ok, Equals, true) 1068 1069 g2 := bootloader.NewGrub(s.rootdir, &bootloader.Options{NoSlashBoot: true, Role: bootloader.RoleRunMode}) 1070 1071 _, err := tab.BootChain(g2, "kernel.snap") 1072 c.Assert(err, ErrorMatches, "not a recovery bootloader") 1073 }