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