github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/gadget/layout_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 gadget_test 21 22 import ( 23 "bytes" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "strings" 30 31 . "gopkg.in/check.v1" 32 33 "github.com/snapcore/snapd/gadget" 34 "github.com/snapcore/snapd/gadget/quantity" 35 "github.com/snapcore/snapd/kernel" 36 ) 37 38 type layoutTestSuite struct { 39 dir string 40 } 41 42 var _ = Suite(&layoutTestSuite{}) 43 44 func (p *layoutTestSuite) SetUpTest(c *C) { 45 p.dir = c.MkDir() 46 } 47 48 var defaultConstraints = gadget.LayoutConstraints{ 49 NonMBRStartOffset: 1 * quantity.OffsetMiB, 50 } 51 52 func (p *layoutTestSuite) TestVolumeSize(c *C) { 53 vol := gadget.Volume{ 54 Structure: []gadget.VolumeStructure{ 55 {Size: 2 * quantity.SizeMiB}, 56 }, 57 } 58 v, err := gadget.LayoutVolume(p.dir, "", &vol, defaultConstraints) 59 c.Assert(err, IsNil) 60 61 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 62 Volume: &gadget.Volume{ 63 Structure: []gadget.VolumeStructure{ 64 {Size: 2 * quantity.SizeMiB}, 65 }, 66 }, 67 Size: 3 * quantity.SizeMiB, 68 RootDir: p.dir, 69 LaidOutStructure: []gadget.LaidOutStructure{{ 70 VolumeStructure: &gadget.VolumeStructure{Size: 2 * quantity.SizeMiB}, 71 StartOffset: 1 * quantity.OffsetMiB, 72 }}, 73 }) 74 } 75 76 func mustParseVolume(c *C, gadgetYaml, volume string) *gadget.Volume { 77 gi, err := gadget.InfoFromGadgetYaml([]byte(gadgetYaml), nil) 78 c.Assert(err, IsNil) 79 v, ok := gi.Volumes[volume] 80 c.Assert(ok, Equals, true, Commentf("volume %q not found in gadget", volume)) 81 return v 82 } 83 84 func (p *layoutTestSuite) TestLayoutVolumeMinimal(c *C) { 85 gadgetYaml := ` 86 volumes: 87 first-image: 88 bootloader: u-boot 89 structure: 90 - type: 00000000-0000-0000-0000-0000deadbeef 91 size: 400M 92 - type: 83,00000000-0000-0000-0000-0000feedface 93 role: system-data 94 size: 100M 95 ` 96 vol := mustParseVolume(c, gadgetYaml, "first-image") 97 c.Assert(vol.Structure, HasLen, 2) 98 99 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 100 c.Assert(err, IsNil) 101 102 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 103 Volume: vol, 104 Size: 501 * quantity.SizeMiB, 105 RootDir: p.dir, 106 LaidOutStructure: []gadget.LaidOutStructure{ 107 { 108 VolumeStructure: &vol.Structure[0], 109 StartOffset: 1 * quantity.OffsetMiB, 110 Index: 0, 111 }, 112 { 113 VolumeStructure: &vol.Structure[1], 114 StartOffset: 401 * quantity.OffsetMiB, 115 Index: 1, 116 }, 117 }, 118 }) 119 } 120 121 func (p *layoutTestSuite) TestLayoutVolumeImplicitOrdering(c *C) { 122 gadgetYaml := ` 123 volumes: 124 first: 125 schema: gpt 126 bootloader: grub 127 structure: 128 - type: 00000000-0000-0000-0000-dd00deadbeef 129 size: 400M 130 - type: 00000000-0000-0000-0000-cc00deadbeef 131 role: system-data 132 size: 500M 133 - type: 00000000-0000-0000-0000-bb00deadbeef 134 size: 100M 135 - type: 00000000-0000-0000-0000-aa00deadbeef 136 size: 100M 137 ` 138 vol := mustParseVolume(c, gadgetYaml, "first") 139 c.Assert(vol.Structure, HasLen, 4) 140 141 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 142 c.Assert(err, IsNil) 143 144 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 145 Volume: vol, 146 Size: 1101 * quantity.SizeMiB, 147 RootDir: p.dir, 148 LaidOutStructure: []gadget.LaidOutStructure{ 149 { 150 VolumeStructure: &vol.Structure[0], 151 StartOffset: 1 * quantity.OffsetMiB, 152 Index: 0, 153 }, 154 { 155 VolumeStructure: &vol.Structure[1], 156 StartOffset: 401 * quantity.OffsetMiB, 157 Index: 1, 158 }, 159 { 160 VolumeStructure: &vol.Structure[2], 161 StartOffset: 901 * quantity.OffsetMiB, 162 Index: 2, 163 }, 164 { 165 VolumeStructure: &vol.Structure[3], 166 StartOffset: 1001 * quantity.OffsetMiB, 167 Index: 3, 168 }, 169 }, 170 }) 171 } 172 173 func (p *layoutTestSuite) TestLayoutVolumeExplicitOrdering(c *C) { 174 gadgetYaml := ` 175 volumes: 176 first: 177 schema: gpt 178 bootloader: grub 179 structure: 180 - type: 00000000-0000-0000-0000-dd00deadbeef 181 size: 400M 182 offset: 800M 183 - type: 00000000-0000-0000-0000-cc00deadbeef 184 role: system-data 185 size: 500M 186 offset: 200M 187 - type: 00000000-0000-0000-0000-bb00deadbeef 188 size: 100M 189 offset: 1200M 190 - type: 00000000-0000-0000-0000-aa00deadbeef 191 size: 100M 192 offset: 1M 193 ` 194 vol := mustParseVolume(c, gadgetYaml, "first") 195 c.Assert(vol.Structure, HasLen, 4) 196 197 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 198 c.Assert(err, IsNil) 199 200 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 201 Volume: vol, 202 Size: 1300 * quantity.SizeMiB, 203 RootDir: p.dir, 204 LaidOutStructure: []gadget.LaidOutStructure{ 205 { 206 VolumeStructure: &vol.Structure[3], 207 StartOffset: 1 * quantity.OffsetMiB, 208 Index: 3, 209 }, 210 { 211 VolumeStructure: &vol.Structure[1], 212 StartOffset: 200 * quantity.OffsetMiB, 213 Index: 1, 214 }, 215 { 216 VolumeStructure: &vol.Structure[0], 217 StartOffset: 800 * quantity.OffsetMiB, 218 Index: 0, 219 }, 220 { 221 VolumeStructure: &vol.Structure[2], 222 StartOffset: 1200 * quantity.OffsetMiB, 223 Index: 2, 224 }, 225 }, 226 }) 227 } 228 229 func (p *layoutTestSuite) TestLayoutVolumeMixedOrdering(c *C) { 230 gadgetYaml := ` 231 volumes: 232 first: 233 schema: gpt 234 bootloader: grub 235 structure: 236 - type: 00000000-0000-0000-0000-dd00deadbeef 237 size: 400M 238 offset: 800M 239 - type: 00000000-0000-0000-0000-cc00deadbeef 240 role: system-data 241 size: 500M 242 offset: 200M 243 - type: 00000000-0000-0000-0000-bb00deadbeef 244 size: 100M 245 - type: 00000000-0000-0000-0000-aa00deadbeef 246 size: 100M 247 offset: 1M 248 ` 249 vol := mustParseVolume(c, gadgetYaml, "first") 250 c.Assert(vol.Structure, HasLen, 4) 251 252 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 253 c.Assert(err, IsNil) 254 255 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 256 Volume: vol, 257 Size: 1200 * quantity.SizeMiB, 258 RootDir: p.dir, 259 LaidOutStructure: []gadget.LaidOutStructure{ 260 { 261 VolumeStructure: &vol.Structure[3], 262 StartOffset: 1 * quantity.OffsetMiB, 263 Index: 3, 264 }, 265 { 266 VolumeStructure: &vol.Structure[1], 267 StartOffset: 200 * quantity.OffsetMiB, 268 Index: 1, 269 }, 270 { 271 VolumeStructure: &vol.Structure[2], 272 StartOffset: 700 * quantity.OffsetMiB, 273 Index: 2, 274 }, 275 { 276 VolumeStructure: &vol.Structure[0], 277 StartOffset: 800 * quantity.OffsetMiB, 278 Index: 0, 279 }, 280 }, 281 }) 282 } 283 284 func (p *layoutTestSuite) TestLayoutVolumeErrorsContentNoSuchFile(c *C) { 285 gadgetYaml := ` 286 volumes: 287 first: 288 schema: gpt 289 bootloader: grub 290 structure: 291 - type: 00000000-0000-0000-0000-dd00deadbeef 292 size: 400M 293 offset: 800M 294 content: 295 - image: foo.img 296 ` 297 vol := mustParseVolume(c, gadgetYaml, "first") 298 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 299 c.Assert(v, IsNil) 300 c.Assert(err, ErrorMatches, `cannot lay out structure #0: content "foo.img":.*no such file or directory`) 301 } 302 303 func makeSizedFile(c *C, path string, size quantity.Size, content []byte) { 304 err := os.MkdirAll(filepath.Dir(path), 0755) 305 c.Assert(err, IsNil) 306 307 f, err := os.Create(path) 308 c.Assert(err, IsNil) 309 defer f.Close() 310 if size != 0 { 311 err = f.Truncate(int64(size)) 312 c.Assert(err, IsNil) 313 } 314 if content != nil { 315 _, err := io.Copy(f, bytes.NewReader(content)) 316 c.Assert(err, IsNil) 317 } 318 } 319 320 func (p *layoutTestSuite) TestLayoutVolumeErrorsContentTooLargeSingle(c *C) { 321 gadgetYaml := ` 322 volumes: 323 first: 324 schema: gpt 325 bootloader: grub 326 structure: 327 - type: 00000000-0000-0000-0000-dd00deadbeef 328 size: 1M 329 content: 330 - image: foo.img 331 ` 332 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), quantity.SizeMiB+1, nil) 333 334 vol := mustParseVolume(c, gadgetYaml, "first") 335 336 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 337 c.Assert(v, IsNil) 338 c.Assert(err, ErrorMatches, `cannot lay out structure #0: content "foo.img" does not fit in the structure`) 339 } 340 341 func (p *layoutTestSuite) TestLayoutVolumeErrorsContentTooLargeMany(c *C) { 342 gadgetYaml := ` 343 volumes: 344 first: 345 schema: gpt 346 bootloader: grub 347 structure: 348 - type: 00000000-0000-0000-0000-dd00deadbeef 349 size: 2M 350 content: 351 - image: foo.img 352 - image: bar.img 353 ` 354 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), quantity.SizeMiB+1, nil) 355 makeSizedFile(c, filepath.Join(p.dir, "bar.img"), quantity.SizeMiB+1, nil) 356 357 vol := mustParseVolume(c, gadgetYaml, "first") 358 359 constraints := gadget.LayoutConstraints{ 360 NonMBRStartOffset: 1 * quantity.OffsetMiB, 361 } 362 v, err := gadget.LayoutVolume(p.dir, "", vol, constraints) 363 c.Assert(v, IsNil) 364 c.Assert(err, ErrorMatches, `cannot lay out structure #0: content "bar.img" does not fit in the structure`) 365 } 366 367 func (p *layoutTestSuite) TestLayoutVolumeErrorsContentTooLargeWithOffset(c *C) { 368 gadgetYaml := ` 369 volumes: 370 first: 371 schema: gpt 372 bootloader: grub 373 structure: 374 - type: 00000000-0000-0000-0000-dd00deadbeef 375 size: 1M 376 content: 377 - image: foo.img 378 # 512kB 379 offset: 524288 380 ` 381 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), quantity.SizeMiB, nil) 382 383 vol := mustParseVolume(c, gadgetYaml, "first") 384 385 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 386 c.Assert(v, IsNil) 387 c.Assert(err, ErrorMatches, `cannot lay out structure #0: content "foo.img" does not fit in the structure`) 388 } 389 390 func (p *layoutTestSuite) TestLayoutVolumeErrorsContentLargerThanDeclared(c *C) { 391 gadgetYaml := ` 392 volumes: 393 first: 394 schema: gpt 395 bootloader: grub 396 structure: 397 - type: 00000000-0000-0000-0000-dd00deadbeef 398 size: 2M 399 content: 400 - image: foo.img 401 size: 1M 402 ` 403 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), quantity.SizeMiB+1, nil) 404 405 vol := mustParseVolume(c, gadgetYaml, "first") 406 407 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 408 c.Assert(v, IsNil) 409 c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot lay out structure #0: content "foo.img" size %v is larger than declared %v`, quantity.SizeMiB+1, quantity.SizeMiB)) 410 } 411 412 func (p *layoutTestSuite) TestLayoutVolumeErrorsContentOverlap(c *C) { 413 gadgetYaml := ` 414 volumes: 415 first: 416 schema: gpt 417 bootloader: grub 418 structure: 419 - type: 00000000-0000-0000-0000-dd00deadbeef 420 size: 2M 421 content: 422 - image: foo.img 423 size: 1M 424 # 512kB 425 offset: 524288 426 - image: bar.img 427 size: 1M 428 offset: 0 429 ` 430 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), quantity.SizeMiB, nil) 431 makeSizedFile(c, filepath.Join(p.dir, "bar.img"), quantity.SizeMiB, nil) 432 433 vol := mustParseVolume(c, gadgetYaml, "first") 434 435 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 436 c.Assert(v, IsNil) 437 c.Assert(err, ErrorMatches, `cannot lay out structure #0: content "foo.img" overlaps with preceding image "bar.img"`) 438 } 439 440 func (p *layoutTestSuite) TestLayoutVolumeContentExplicitOrder(c *C) { 441 gadgetYaml := ` 442 volumes: 443 first: 444 schema: gpt 445 bootloader: grub 446 structure: 447 - type: 00000000-0000-0000-0000-0000deadbeef 448 size: 2M 449 content: 450 - image: foo.img 451 size: 1M 452 offset: 1M 453 - image: bar.img 454 size: 1M 455 offset: 0 456 ` 457 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), quantity.SizeMiB, nil) 458 makeSizedFile(c, filepath.Join(p.dir, "bar.img"), quantity.SizeMiB, nil) 459 460 vol := mustParseVolume(c, gadgetYaml, "first") 461 c.Assert(vol.Structure, HasLen, 1) 462 c.Assert(vol.Structure[0].Content, HasLen, 2) 463 464 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 465 c.Assert(err, IsNil) 466 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 467 Volume: vol, 468 Size: 3 * quantity.SizeMiB, 469 RootDir: p.dir, 470 LaidOutStructure: []gadget.LaidOutStructure{ 471 { 472 VolumeStructure: &vol.Structure[0], 473 StartOffset: 1 * quantity.OffsetMiB, 474 LaidOutContent: []gadget.LaidOutContent{ 475 { 476 VolumeContent: &vol.Structure[0].Content[1], 477 StartOffset: 1 * quantity.OffsetMiB, 478 Size: quantity.SizeMiB, 479 Index: 1, 480 }, 481 { 482 VolumeContent: &vol.Structure[0].Content[0], 483 StartOffset: 2 * quantity.OffsetMiB, 484 Size: quantity.SizeMiB, 485 Index: 0, 486 }, 487 }, 488 }, 489 }, 490 }) 491 } 492 493 func (p *layoutTestSuite) TestLayoutVolumeContentImplicitOrder(c *C) { 494 gadgetYaml := ` 495 volumes: 496 first: 497 schema: gpt 498 bootloader: grub 499 structure: 500 - type: 00000000-0000-0000-0000-0000deadbeef 501 size: 2M 502 content: 503 - image: foo.img 504 size: 1M 505 - image: bar.img 506 size: 1M 507 ` 508 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), quantity.SizeMiB, nil) 509 makeSizedFile(c, filepath.Join(p.dir, "bar.img"), quantity.SizeMiB, nil) 510 511 vol := mustParseVolume(c, gadgetYaml, "first") 512 c.Assert(vol.Structure, HasLen, 1) 513 c.Assert(vol.Structure[0].Content, HasLen, 2) 514 515 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 516 c.Assert(err, IsNil) 517 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 518 Volume: vol, 519 Size: 3 * quantity.SizeMiB, 520 RootDir: p.dir, 521 LaidOutStructure: []gadget.LaidOutStructure{ 522 { 523 VolumeStructure: &vol.Structure[0], 524 StartOffset: 1 * quantity.OffsetMiB, 525 LaidOutContent: []gadget.LaidOutContent{ 526 { 527 VolumeContent: &vol.Structure[0].Content[0], 528 StartOffset: 1 * quantity.OffsetMiB, 529 Size: quantity.SizeMiB, 530 Index: 0, 531 }, 532 { 533 VolumeContent: &vol.Structure[0].Content[1], 534 StartOffset: 2 * quantity.OffsetMiB, 535 Size: quantity.SizeMiB, 536 Index: 1, 537 }, 538 }, 539 }, 540 }, 541 }) 542 } 543 544 func (p *layoutTestSuite) TestLayoutVolumeContentImplicitSize(c *C) { 545 gadgetYaml := ` 546 volumes: 547 first: 548 schema: gpt 549 bootloader: grub 550 structure: 551 - type: 00000000-0000-0000-0000-0000deadbeef 552 size: 2M 553 content: 554 - image: foo.img 555 ` 556 size1_5MiB := quantity.SizeMiB + quantity.SizeMiB/2 557 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), size1_5MiB, nil) 558 559 vol := mustParseVolume(c, gadgetYaml, "first") 560 c.Assert(vol.Structure, HasLen, 1) 561 c.Assert(vol.Structure[0].Content, HasLen, 1) 562 563 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 564 c.Assert(err, IsNil) 565 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 566 Volume: vol, 567 Size: 3 * quantity.SizeMiB, 568 RootDir: p.dir, 569 LaidOutStructure: []gadget.LaidOutStructure{ 570 { 571 VolumeStructure: &vol.Structure[0], 572 StartOffset: 1 * quantity.OffsetMiB, 573 LaidOutContent: []gadget.LaidOutContent{ 574 { 575 VolumeContent: &vol.Structure[0].Content[0], 576 StartOffset: 1 * quantity.OffsetMiB, 577 Size: size1_5MiB, 578 }, 579 }, 580 }, 581 }, 582 }) 583 } 584 585 func (p *layoutTestSuite) TestLayoutVolumeContentNonBare(c *C) { 586 gadgetYaml := ` 587 volumes: 588 first: 589 schema: gpt 590 bootloader: grub 591 structure: 592 - type: 00000000-0000-0000-0000-0000deadbeef 593 filesystem: ext4 594 size: 2M 595 content: 596 - source: foo.txt 597 target: /boot 598 ` 599 makeSizedFile(c, filepath.Join(p.dir, "foo.txt"), 0, []byte("foobar\n")) 600 601 vol := mustParseVolume(c, gadgetYaml, "first") 602 c.Assert(vol.Structure, HasLen, 1) 603 c.Assert(vol.Structure[0].Content, HasLen, 1) 604 605 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 606 c.Assert(err, IsNil) 607 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 608 Volume: vol, 609 Size: 3 * quantity.SizeMiB, 610 RootDir: p.dir, 611 LaidOutStructure: []gadget.LaidOutStructure{ 612 { 613 StartOffset: 1 * quantity.OffsetMiB, 614 VolumeStructure: &vol.Structure[0], 615 ResolvedContent: []gadget.ResolvedContent{ 616 { 617 VolumeContent: &gadget.VolumeContent{ 618 UnresolvedSource: "foo.txt", 619 Target: "/boot", 620 }, 621 ResolvedSource: filepath.Join(p.dir, "foo.txt"), 622 }, 623 }, 624 }, 625 }, 626 }) 627 } 628 629 func (p *layoutTestSuite) TestLayoutVolumeConstraintsChange(c *C) { 630 gadgetYaml := ` 631 volumes: 632 first: 633 schema: gpt 634 bootloader: grub 635 structure: 636 - role: mbr 637 type: bare 638 size: 446 639 offset: 0 640 - type: 00000000-0000-0000-0000-0000deadbeef 641 filesystem: ext4 642 size: 2M 643 content: 644 - source: foo.txt 645 target: /boot 646 ` 647 resolvedContent := []gadget.ResolvedContent{ 648 { 649 VolumeContent: &gadget.VolumeContent{ 650 UnresolvedSource: "foo.txt", 651 Target: "/boot", 652 }, 653 ResolvedSource: filepath.Join(p.dir, "foo.txt"), 654 }, 655 } 656 657 makeSizedFile(c, filepath.Join(p.dir, "foo.txt"), 0, []byte("foobar\n")) 658 659 vol := mustParseVolume(c, gadgetYaml, "first") 660 c.Assert(vol.Structure, HasLen, 2) 661 c.Assert(vol.Structure[1].Content, HasLen, 1) 662 663 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 664 c.Assert(err, IsNil) 665 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 666 Volume: vol, 667 Size: 3 * quantity.SizeMiB, 668 RootDir: p.dir, 669 LaidOutStructure: []gadget.LaidOutStructure{ 670 { 671 VolumeStructure: &vol.Structure[0], 672 StartOffset: 0, 673 Index: 0, 674 }, 675 { 676 VolumeStructure: &vol.Structure[1], 677 StartOffset: 1 * quantity.OffsetMiB, 678 Index: 1, 679 ResolvedContent: resolvedContent, 680 }, 681 }, 682 }) 683 684 // still valid 685 constraints := gadget.LayoutConstraints{ 686 // 512kiB 687 NonMBRStartOffset: 512 * quantity.OffsetKiB, 688 } 689 v, err = gadget.LayoutVolume(p.dir, "", vol, constraints) 690 c.Assert(err, IsNil) 691 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 692 Volume: vol, 693 Size: 2*quantity.SizeMiB + 512*quantity.SizeKiB, 694 RootDir: p.dir, 695 LaidOutStructure: []gadget.LaidOutStructure{ 696 { 697 VolumeStructure: &vol.Structure[0], 698 StartOffset: 0, 699 Index: 0, 700 }, 701 { 702 VolumeStructure: &vol.Structure[1], 703 StartOffset: 512 * quantity.OffsetKiB, 704 Index: 1, 705 ResolvedContent: resolvedContent, 706 }, 707 }, 708 }) 709 710 // constraints would make a non MBR structure overlap with MBR, but 711 // structures start one after another unless offset is specified 712 // explicitly 713 constraintsBad := gadget.LayoutConstraints{ 714 NonMBRStartOffset: 400, 715 } 716 v, err = gadget.LayoutVolume(p.dir, "", vol, constraintsBad) 717 c.Assert(err, IsNil) 718 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 719 Volume: vol, 720 Size: 2*quantity.SizeMiB + 446, 721 RootDir: p.dir, 722 LaidOutStructure: []gadget.LaidOutStructure{ 723 { 724 VolumeStructure: &vol.Structure[0], 725 Index: 0, 726 }, 727 { 728 VolumeStructure: &vol.Structure[1], 729 StartOffset: 446, 730 Index: 1, 731 ResolvedContent: resolvedContent, 732 }, 733 }, 734 }) 735 } 736 737 func (p *layoutTestSuite) TestLayoutVolumeMBRImplicitConstraints(c *C) { 738 gadgetYaml := ` 739 volumes: 740 first: 741 schema: gpt 742 bootloader: grub 743 structure: 744 - name: mbr 745 type: bare 746 role: mbr 747 size: 446 748 - name: other 749 type: 00000000-0000-0000-0000-0000deadbeef 750 size: 1M 751 ` 752 vol := mustParseVolume(c, gadgetYaml, "first") 753 c.Assert(vol.Structure, HasLen, 2) 754 755 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 756 c.Assert(err, IsNil) 757 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 758 Volume: vol, 759 Size: 2 * quantity.SizeMiB, 760 RootDir: p.dir, 761 LaidOutStructure: []gadget.LaidOutStructure{ 762 { 763 // MBR 764 VolumeStructure: &vol.Structure[0], 765 StartOffset: 0, 766 Index: 0, 767 }, { 768 VolumeStructure: &vol.Structure[1], 769 StartOffset: 1 * quantity.OffsetMiB, 770 Index: 1, 771 }, 772 }, 773 }) 774 } 775 776 func (p *layoutTestSuite) TestLayoutVolumeOffsetWriteAll(c *C) { 777 var gadgetYaml = ` 778 volumes: 779 pc: 780 bootloader: grub 781 structure: 782 - name: mbr 783 type: mbr 784 size: 440 785 - name: foo 786 type: DA,21686148-6449-6E6F-744E-656564454649 787 size: 1M 788 offset: 1M 789 offset-write: mbr+92 790 content: 791 - image: foo.img 792 offset-write: bar+10 793 - name: bar 794 type: DA,21686148-6449-6E6F-744E-656564454649 795 size: 1M 796 offset-write: 600 797 content: 798 - image: bar.img 799 offset-write: 450 800 ` 801 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), 200*quantity.SizeKiB, []byte("")) 802 makeSizedFile(c, filepath.Join(p.dir, "bar.img"), 150*quantity.SizeKiB, []byte("")) 803 804 vol := mustParseVolume(c, gadgetYaml, "pc") 805 c.Assert(vol.Structure, HasLen, 3) 806 807 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 808 c.Assert(err, IsNil) 809 c.Assert(v, DeepEquals, &gadget.LaidOutVolume{ 810 Volume: vol, 811 Size: 3 * quantity.SizeMiB, 812 RootDir: p.dir, 813 LaidOutStructure: []gadget.LaidOutStructure{ 814 { 815 // mbr 816 VolumeStructure: &vol.Structure[0], 817 StartOffset: 0, 818 Index: 0, 819 }, { 820 // foo 821 VolumeStructure: &vol.Structure[1], 822 StartOffset: 1 * quantity.OffsetMiB, 823 Index: 1, 824 // break for gofmt < 1.11 825 AbsoluteOffsetWrite: asOffsetPtr(92), 826 LaidOutContent: []gadget.LaidOutContent{ 827 { 828 VolumeContent: &vol.Structure[1].Content[0], 829 Size: 200 * quantity.SizeKiB, 830 StartOffset: 1 * quantity.OffsetMiB, 831 // offset-write: bar+10 832 AbsoluteOffsetWrite: asOffsetPtr(2*quantity.OffsetMiB + 10), 833 }, 834 }, 835 }, { 836 // bar 837 VolumeStructure: &vol.Structure[2], 838 StartOffset: 2 * quantity.OffsetMiB, 839 Index: 2, 840 // break for gofmt < 1.11 841 AbsoluteOffsetWrite: asOffsetPtr(600), 842 LaidOutContent: []gadget.LaidOutContent{ 843 { 844 VolumeContent: &vol.Structure[2].Content[0], 845 Size: 150 * quantity.SizeKiB, 846 StartOffset: 2 * quantity.OffsetMiB, 847 // offset-write: bar+10 848 AbsoluteOffsetWrite: asOffsetPtr(450), 849 }, 850 }, 851 }, 852 }, 853 }) 854 } 855 856 func (p *layoutTestSuite) TestLayoutVolumeOffsetWriteBadRelativeTo(c *C) { 857 // define volumes explicitly as those would not pass validation 858 volBadStructure := gadget.Volume{ 859 Structure: []gadget.VolumeStructure{ 860 { 861 Name: "foo", 862 Type: "DA,21686148-6449-6E6F-744E-656564454649", 863 Size: 1 * quantity.SizeMiB, 864 OffsetWrite: &gadget.RelativeOffset{ 865 RelativeTo: "bar", 866 Offset: 10, 867 }, 868 }, 869 }, 870 } 871 volBadContent := gadget.Volume{ 872 Structure: []gadget.VolumeStructure{ 873 { 874 Name: "foo", 875 Type: "DA,21686148-6449-6E6F-744E-656564454649", 876 Size: 1 * quantity.SizeMiB, 877 Content: []gadget.VolumeContent{ 878 { 879 Image: "foo.img", 880 OffsetWrite: &gadget.RelativeOffset{ 881 RelativeTo: "bar", 882 Offset: 10, 883 }, 884 }, 885 }, 886 }, 887 }, 888 } 889 890 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), 200*quantity.SizeKiB, []byte("")) 891 892 v, err := gadget.LayoutVolume(p.dir, "", &volBadStructure, defaultConstraints) 893 c.Check(v, IsNil) 894 c.Check(err, ErrorMatches, `cannot resolve offset-write of structure #0 \("foo"\): refers to an unknown structure "bar"`) 895 896 v, err = gadget.LayoutVolume(p.dir, "", &volBadContent, defaultConstraints) 897 c.Check(v, IsNil) 898 c.Check(err, ErrorMatches, `cannot resolve offset-write of structure #0 \("foo"\) content "foo.img": refers to an unknown structure "bar"`) 899 } 900 901 func (p *layoutTestSuite) TestLayoutVolumeOffsetWriteEnlargesVolume(c *C) { 902 var gadgetYamlStructure = ` 903 volumes: 904 pc: 905 bootloader: grub 906 structure: 907 - name: mbr 908 type: mbr 909 size: 440 910 - name: foo 911 type: DA,21686148-6449-6E6F-744E-656564454649 912 size: 1M 913 offset: 1M 914 # 1GB 915 offset-write: mbr+1073741824 916 917 ` 918 vol := mustParseVolume(c, gadgetYamlStructure, "pc") 919 920 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 921 c.Assert(err, IsNil) 922 // offset-write is at 1GB 923 c.Check(v.Size, Equals, 1*quantity.SizeGiB+gadget.SizeLBA48Pointer) 924 925 var gadgetYamlContent = ` 926 volumes: 927 pc: 928 bootloader: grub 929 structure: 930 - name: mbr 931 type: mbr 932 size: 440 933 - name: foo 934 type: DA,21686148-6449-6E6F-744E-656564454649 935 size: 1M 936 offset: 1M 937 content: 938 - image: foo.img 939 # 2GB 940 offset-write: mbr+2147483648 941 - image: bar.img 942 # 1GB 943 offset-write: mbr+1073741824 944 - image: baz.img 945 # 3GB 946 offset-write: mbr+3221225472 947 948 ` 949 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), 200*quantity.SizeKiB, []byte("")) 950 makeSizedFile(c, filepath.Join(p.dir, "bar.img"), 150*quantity.SizeKiB, []byte("")) 951 makeSizedFile(c, filepath.Join(p.dir, "baz.img"), 100*quantity.SizeKiB, []byte("")) 952 953 vol = mustParseVolume(c, gadgetYamlContent, "pc") 954 955 v, err = gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 956 c.Assert(err, IsNil) 957 // foo.img offset-write is at 3GB 958 c.Check(v.Size, Equals, 3*quantity.SizeGiB+gadget.SizeLBA48Pointer) 959 } 960 961 func (p *layoutTestSuite) TestLayoutVolumePartialNoSuchFile(c *C) { 962 gadgetYaml := ` 963 volumes: 964 first: 965 schema: gpt 966 bootloader: grub 967 structure: 968 - type: 00000000-0000-0000-0000-dd00deadbeef 969 size: 400M 970 offset: 800M 971 content: 972 - image: foo.img 973 ` 974 vol := mustParseVolume(c, gadgetYaml, "first") 975 c.Assert(vol.Structure, HasLen, 1) 976 977 v, err := gadget.LayoutVolumePartially(vol, defaultConstraints) 978 c.Assert(v, DeepEquals, &gadget.PartiallyLaidOutVolume{ 979 Volume: vol, 980 LaidOutStructure: []gadget.LaidOutStructure{ 981 { 982 VolumeStructure: &vol.Structure[0], 983 StartOffset: 800 * quantity.OffsetMiB, 984 Index: 0, 985 }, 986 }, 987 }) 988 c.Assert(err, IsNil) 989 } 990 991 func (p *layoutTestSuite) TestLaidOutStructureShift(c *C) { 992 var gadgetYamlContent = ` 993 volumes: 994 pc: 995 bootloader: grub 996 structure: 997 - name: foo 998 type: DA,21686148-6449-6E6F-744E-656564454649 999 size: 1M 1000 offset: 1M 1001 content: 1002 - image: foo.img 1003 - image: bar.img 1004 # 300KB 1005 offset: 307200 1006 1007 ` 1008 makeSizedFile(c, filepath.Join(p.dir, "foo.img"), 200*quantity.SizeKiB, []byte("")) 1009 makeSizedFile(c, filepath.Join(p.dir, "bar.img"), 150*quantity.SizeKiB, []byte("")) 1010 1011 vol := mustParseVolume(c, gadgetYamlContent, "pc") 1012 1013 v, err := gadget.LayoutVolume(p.dir, "", vol, defaultConstraints) 1014 c.Assert(err, IsNil) 1015 c.Assert(v.LaidOutStructure, HasLen, 1) 1016 c.Assert(v.LaidOutStructure[0].LaidOutContent, HasLen, 2) 1017 1018 ps := v.LaidOutStructure[0] 1019 1020 c.Assert(ps, DeepEquals, gadget.LaidOutStructure{ 1021 // foo 1022 VolumeStructure: &vol.Structure[0], 1023 StartOffset: 1 * quantity.OffsetMiB, 1024 Index: 0, 1025 LaidOutContent: []gadget.LaidOutContent{ 1026 { 1027 VolumeContent: &vol.Structure[0].Content[0], 1028 Size: 200 * quantity.SizeKiB, 1029 StartOffset: 1 * quantity.OffsetMiB, 1030 Index: 0, 1031 }, { 1032 VolumeContent: &vol.Structure[0].Content[1], 1033 Size: 150 * quantity.SizeKiB, 1034 StartOffset: 1*quantity.OffsetMiB + 300*quantity.OffsetKiB, 1035 Index: 1, 1036 }, 1037 }, 1038 }) 1039 1040 shiftedTo0 := gadget.ShiftStructureTo(ps, 0) 1041 c.Assert(shiftedTo0, DeepEquals, gadget.LaidOutStructure{ 1042 // foo 1043 VolumeStructure: &vol.Structure[0], 1044 StartOffset: 0, 1045 Index: 0, 1046 LaidOutContent: []gadget.LaidOutContent{ 1047 { 1048 VolumeContent: &vol.Structure[0].Content[0], 1049 Size: 200 * quantity.SizeKiB, 1050 StartOffset: 0, 1051 Index: 0, 1052 }, { 1053 VolumeContent: &vol.Structure[0].Content[1], 1054 Size: 150 * quantity.SizeKiB, 1055 StartOffset: 300 * quantity.OffsetKiB, 1056 Index: 1, 1057 }, 1058 }, 1059 }) 1060 1061 shiftedTo2M := gadget.ShiftStructureTo(ps, 2*quantity.OffsetMiB) 1062 c.Assert(shiftedTo2M, DeepEquals, gadget.LaidOutStructure{ 1063 // foo 1064 VolumeStructure: &vol.Structure[0], 1065 StartOffset: 2 * quantity.OffsetMiB, 1066 Index: 0, 1067 LaidOutContent: []gadget.LaidOutContent{ 1068 { 1069 VolumeContent: &vol.Structure[0].Content[0], 1070 Size: 200 * quantity.SizeKiB, 1071 StartOffset: 2 * quantity.OffsetMiB, 1072 Index: 0, 1073 }, { 1074 VolumeContent: &vol.Structure[0].Content[1], 1075 Size: 150 * quantity.SizeKiB, 1076 StartOffset: 2*quantity.OffsetMiB + 300*quantity.OffsetKiB, 1077 Index: 1, 1078 }, 1079 }, 1080 }) 1081 } 1082 1083 func mockKernel(c *C, kernelYaml string, filesWithContent map[string]string) string { 1084 // sanity 1085 _, err := kernel.InfoFromKernelYaml([]byte(kernelYaml)) 1086 c.Assert(err, IsNil) 1087 1088 kernelRootDir := c.MkDir() 1089 err = os.MkdirAll(filepath.Join(kernelRootDir, "meta"), 0755) 1090 c.Assert(err, IsNil) 1091 err = ioutil.WriteFile(filepath.Join(kernelRootDir, "meta/kernel.yaml"), []byte(kernelYaml), 0644) 1092 c.Assert(err, IsNil) 1093 1094 for fname, content := range filesWithContent { 1095 p := filepath.Join(kernelRootDir, fname) 1096 err = os.MkdirAll(filepath.Dir(p), 0755) 1097 c.Assert(err, IsNil) 1098 err = ioutil.WriteFile(p, []byte(content), 0644) 1099 c.Assert(err, IsNil) 1100 } 1101 1102 // ensure we have valid kernel.yaml in our tests 1103 err = kernel.Validate(kernelRootDir) 1104 c.Assert(err, IsNil) 1105 1106 return kernelRootDir 1107 } 1108 1109 var gadgetYamlWithKernelRef = ` 1110 volumes: 1111 pi: 1112 bootloader: u-boot 1113 structure: 1114 - type: 00000000-0000-0000-0000-dd00deadbeef 1115 filesystem: vfat 1116 filesystem-label: system-boot 1117 size: 128M 1118 content: 1119 - source: $kernel:dtbs/boot-assets/ 1120 target: / 1121 - source: $kernel:dtbs/some-file 1122 target: / 1123 - source: file-from-gadget 1124 target: / 1125 - source: dir-from-gadget/ 1126 target: / 1127 ` 1128 1129 func (p *layoutTestSuite) TestResolveContentPathsNotInWantedAssets(c *C) { 1130 vol := mustParseVolume(c, gadgetYamlWithKernelRef, "pi") 1131 c.Assert(vol.Structure, HasLen, 1) 1132 1133 kernelSnapDir := c.MkDir() 1134 _, err := gadget.LayoutVolume(p.dir, kernelSnapDir, vol, defaultConstraints) 1135 c.Assert(err, ErrorMatches, `cannot resolve content for structure #0 at index 0: cannot find "dtbs" in kernel info from "/.*"`) 1136 } 1137 1138 func (p *layoutTestSuite) TestResolveContentPathsErrorInKernelRef(c *C) { 1139 // create invalid kernel ref 1140 s := strings.Replace(gadgetYamlWithKernelRef, "$kernel:dtbs", "$kernel:-invalid-kernel-ref", -1) 1141 // Note that mustParseVolume does not call ValidateContent() which 1142 // would be needed to validate "$kernel:" refs. 1143 vol := mustParseVolume(c, s, "pi") 1144 c.Assert(vol.Structure, HasLen, 1) 1145 1146 kernelSnapDir := c.MkDir() 1147 _, err := gadget.LayoutVolume(p.dir, kernelSnapDir, vol, defaultConstraints) 1148 c.Assert(err, ErrorMatches, `cannot resolve content for structure #0 at index 0: cannot parse kernel ref: invalid asset name in kernel ref "\$kernel:-invalid-kernel-ref/boot-assets/"`) 1149 } 1150 1151 func (p *layoutTestSuite) TestResolveContentPathsNotInWantedeContent(c *C) { 1152 kernelYaml := ` 1153 assets: 1154 dtbs: 1155 update: true 1156 content: 1157 - dtbs/ 1158 ` 1159 1160 vol := mustParseVolume(c, gadgetYamlWithKernelRef, "pi") 1161 c.Assert(vol.Structure, HasLen, 1) 1162 1163 kernelSnapDir := mockKernel(c, kernelYaml, map[string]string{ 1164 "dtbs/foo.dtb": "foo.dtb content", 1165 }) 1166 _, err := gadget.LayoutVolume(p.dir, kernelSnapDir, vol, defaultConstraints) 1167 c.Assert(err, ErrorMatches, `cannot resolve content for structure #0 at index 0: cannot find wanted kernel content "boot-assets/" in "/.*"`) 1168 } 1169 1170 func (p *layoutTestSuite) TestResolveContentPaths(c *C) { 1171 kernelYaml := ` 1172 assets: 1173 dtbs: 1174 update: true 1175 content: 1176 - boot-assets/ 1177 - some-file 1178 ` 1179 vol := mustParseVolume(c, gadgetYamlWithKernelRef, "pi") 1180 c.Assert(vol.Structure, HasLen, 1) 1181 1182 kernelSnapFiles := map[string]string{ 1183 "boot-assets/foo": "foo-content", 1184 "some-file": "some-file content", 1185 } 1186 kernelSnapDir := mockKernel(c, kernelYaml, kernelSnapFiles) 1187 lv, err := gadget.LayoutVolume(p.dir, kernelSnapDir, vol, defaultConstraints) 1188 c.Assert(err, IsNil) 1189 // Volume.Content is unchanged 1190 c.Assert(lv.Structure, HasLen, 1) 1191 c.Check(lv.Structure[0].Content, DeepEquals, []gadget.VolumeContent{ 1192 { 1193 UnresolvedSource: "$kernel:dtbs/boot-assets/", 1194 Target: "/", 1195 }, 1196 { 1197 UnresolvedSource: "$kernel:dtbs/some-file", 1198 Target: "/", 1199 }, 1200 { 1201 UnresolvedSource: "file-from-gadget", 1202 Target: "/", 1203 }, 1204 { 1205 UnresolvedSource: "dir-from-gadget/", 1206 Target: "/", 1207 }, 1208 }) 1209 // and the LaidOutSturctures ResolvedContent has the correct paths 1210 c.Assert(lv.LaidOutStructure, HasLen, 1) 1211 c.Check(lv.LaidOutStructure[0].ResolvedContent, DeepEquals, []gadget.ResolvedContent{ 1212 { 1213 VolumeContent: &lv.Structure[0].Content[0], 1214 ResolvedSource: filepath.Join(kernelSnapDir, "boot-assets/") + "/", 1215 KernelUpdate: true, 1216 }, 1217 { 1218 VolumeContent: &lv.Structure[0].Content[1], 1219 ResolvedSource: filepath.Join(kernelSnapDir, "some-file"), 1220 KernelUpdate: true, 1221 }, 1222 { 1223 VolumeContent: &lv.Structure[0].Content[2], 1224 ResolvedSource: filepath.Join(p.dir, "file-from-gadget"), 1225 }, 1226 { 1227 VolumeContent: &lv.Structure[0].Content[3], 1228 ResolvedSource: filepath.Join(p.dir, "dir-from-gadget") + "/", 1229 }, 1230 }) 1231 } 1232 1233 func (p *layoutTestSuite) TestResolveContentPathsLp1907056(c *C) { 1234 var gadgetYamlWithKernelRef = ` 1235 volumes: 1236 pi: 1237 schema: mbr 1238 bootloader: u-boot 1239 structure: 1240 - name: ubuntu-seed 1241 role: system-seed 1242 filesystem: vfat 1243 type: 0C 1244 size: 1200M 1245 content: 1246 - source: $kernel:pidtbs/dtbs/broadcom/ 1247 target: / 1248 - source: $kernel:pidtbs/dtbs/overlays/ 1249 target: /overlays 1250 - source: boot-assets/ 1251 target: / 1252 ` 1253 1254 kernelYaml := ` 1255 assets: 1256 pidtbs: 1257 update: true 1258 content: 1259 - dtbs/ 1260 ` 1261 vol := mustParseVolume(c, gadgetYamlWithKernelRef, "pi") 1262 c.Assert(vol.Structure, HasLen, 1) 1263 1264 kernelSnapDir := mockKernel(c, kernelYaml, map[string]string{ 1265 "dtbs/foo.dtb": "foo.dtb content", 1266 }) 1267 lv, err := gadget.LayoutVolume(p.dir, kernelSnapDir, vol, defaultConstraints) 1268 c.Assert(err, IsNil) 1269 // Volume.Content is unchanged 1270 c.Assert(lv.Structure, HasLen, 1) 1271 c.Check(lv.Structure[0].Content, DeepEquals, []gadget.VolumeContent{ 1272 { 1273 UnresolvedSource: "$kernel:pidtbs/dtbs/broadcom/", 1274 Target: "/", 1275 }, 1276 { 1277 UnresolvedSource: "$kernel:pidtbs/dtbs/overlays/", 1278 Target: "/overlays", 1279 }, 1280 { 1281 UnresolvedSource: "boot-assets/", 1282 Target: "/", 1283 }, 1284 }) 1285 // and the LaidOutSturctures ResolvedContent has the correct paths 1286 c.Assert(lv.LaidOutStructure, HasLen, 1) 1287 c.Check(lv.LaidOutStructure[0].ResolvedContent, DeepEquals, []gadget.ResolvedContent{ 1288 { 1289 VolumeContent: &lv.Structure[0].Content[0], 1290 ResolvedSource: filepath.Join(kernelSnapDir, "dtbs/broadcom/") + "/", 1291 KernelUpdate: true, 1292 }, 1293 { 1294 VolumeContent: &lv.Structure[0].Content[1], 1295 ResolvedSource: filepath.Join(kernelSnapDir, "dtbs/overlays") + "/", 1296 KernelUpdate: true, 1297 }, 1298 { 1299 VolumeContent: &lv.Structure[0].Content[2], 1300 ResolvedSource: filepath.Join(p.dir, "boot-assets") + "/", 1301 }, 1302 }) 1303 } 1304 1305 func (p *layoutTestSuite) TestResolveContentSamePrefixErrors(c *C) { 1306 var gadgetYamlWithKernelRef = ` 1307 volumes: 1308 pi: 1309 schema: mbr 1310 bootloader: u-boot 1311 structure: 1312 - name: ubuntu-seed 1313 role: system-seed 1314 filesystem: vfat 1315 type: 0C 1316 size: 1200M 1317 content: 1318 - source: $kernel:dtbs/a 1319 target: / 1320 - source: $kernel:dtbs/ab 1321 target: / 1322 ` 1323 kernelYaml := ` 1324 assets: 1325 dtbs: 1326 update: true 1327 content: 1328 - a 1329 ` 1330 vol := mustParseVolume(c, gadgetYamlWithKernelRef, "pi") 1331 c.Assert(vol.Structure, HasLen, 1) 1332 1333 kernelSnapDir := mockKernel(c, kernelYaml, map[string]string{ 1334 "a/foo.dtb": "foo.dtb content", 1335 }) 1336 _, err := gadget.LayoutVolume(p.dir, kernelSnapDir, vol, defaultConstraints) 1337 c.Assert(err, ErrorMatches, `.*: cannot find wanted kernel content "ab" in.*`) 1338 }