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