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