github.com/rigado/snapd@v2.42.5-go-mod+incompatible/gadget/raw_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 package gadget_test 20 21 import ( 22 "errors" 23 "io" 24 "os" 25 "path/filepath" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/gadget" 30 "github.com/snapcore/snapd/osutil" 31 ) 32 33 type rawTestSuite struct { 34 dir string 35 backup string 36 } 37 38 var _ = Suite(&rawTestSuite{}) 39 40 func (r *rawTestSuite) SetUpTest(c *C) { 41 r.dir = c.MkDir() 42 r.backup = c.MkDir() 43 } 44 45 func openSizedFile(c *C, path string, size gadget.Size) *os.File { 46 f, err := os.Create(path) 47 c.Assert(err, IsNil) 48 49 if size != 0 { 50 err = f.Truncate(int64(size)) 51 c.Assert(err, IsNil) 52 } 53 54 return f 55 } 56 57 type mutateWrite struct { 58 what []byte 59 off int64 60 } 61 62 func mutateFile(c *C, path string, size gadget.Size, writes []mutateWrite) { 63 out := openSizedFile(c, path, size) 64 for _, op := range writes { 65 _, err := out.WriteAt(op.what, op.off) 66 c.Assert(err, IsNil) 67 } 68 } 69 70 func (r *rawTestSuite) TestRawWriterHappy(c *C) { 71 72 out := openSizedFile(c, filepath.Join(r.dir, "out.img"), 2048) 73 defer out.Close() 74 75 makeSizedFile(c, filepath.Join(r.dir, "foo.img"), 128, []byte("foo foo foo")) 76 makeSizedFile(c, filepath.Join(r.dir, "bar.img"), 128, []byte("bar bar bar")) 77 78 ps := &gadget.LaidOutStructure{ 79 VolumeStructure: &gadget.VolumeStructure{ 80 Size: 2048, 81 }, 82 LaidOutContent: []gadget.LaidOutContent{ 83 { 84 VolumeContent: &gadget.VolumeContent{ 85 Image: "foo.img", 86 }, 87 StartOffset: 0, 88 Size: 128, 89 Index: 0, 90 }, { 91 VolumeContent: &gadget.VolumeContent{ 92 Image: "bar.img", 93 }, 94 StartOffset: 1024, 95 Size: 128, 96 Index: 1, 97 }, 98 }, 99 } 100 rw, err := gadget.NewRawStructureWriter(r.dir, ps) 101 c.Assert(err, IsNil) 102 c.Assert(rw, NotNil) 103 104 err = rw.Write(out) 105 c.Assert(err, IsNil) 106 107 expectedPath := filepath.Join(r.dir, "expected.img") 108 mutateFile(c, expectedPath, 2048, []mutateWrite{ 109 {[]byte("foo foo foo"), 0}, 110 {[]byte("bar bar bar"), 1024}, 111 }) 112 expected, err := os.Open(expectedPath) 113 c.Assert(err, IsNil) 114 defer expected.Close() 115 116 // rewind 117 _, err = out.Seek(0, io.SeekStart) 118 c.Assert(err, IsNil) 119 _, err = expected.Seek(0, io.SeekStart) 120 c.Assert(err, IsNil) 121 122 c.Check(osutil.StreamsEqual(out, expected), Equals, true) 123 } 124 125 func (r *rawTestSuite) TestRawWriterNoFile(c *C) { 126 127 ps := &gadget.LaidOutStructure{ 128 VolumeStructure: &gadget.VolumeStructure{ 129 Size: 2048, 130 }, 131 LaidOutContent: []gadget.LaidOutContent{ 132 { 133 VolumeContent: &gadget.VolumeContent{ 134 Image: "foo.img", 135 }, 136 StartOffset: 0, 137 }, 138 }, 139 } 140 rw, err := gadget.NewRawStructureWriter(r.dir, ps) 141 c.Assert(err, IsNil) 142 c.Assert(rw, NotNil) 143 144 out := openSizedFile(c, filepath.Join(r.dir, "out.img"), 2048) 145 defer out.Close() 146 147 err = rw.Write(out) 148 c.Assert(err, ErrorMatches, "failed to write image.* cannot open image file:.* no such file or directory") 149 } 150 151 type mockWriteSeeker struct { 152 write func(b []byte) (n int, err error) 153 seek func(offset int64, whence int) (ret int64, err error) 154 } 155 156 func (m *mockWriteSeeker) Write(b []byte) (n int, err error) { 157 if m.write != nil { 158 return m.write(b) 159 } 160 return len(b), nil 161 } 162 163 func (m *mockWriteSeeker) Seek(offset int64, whence int) (ret int64, err error) { 164 if m.seek != nil { 165 return m.seek(offset, whence) 166 } 167 return offset, nil 168 } 169 170 func (r *rawTestSuite) TestRawWriterFailInWriteSeeker(c *C) { 171 out := &mockWriteSeeker{ 172 write: func(b []byte) (n int, err error) { 173 c.Logf("write write\n") 174 return 0, errors.New("failed") 175 }, 176 } 177 makeSizedFile(c, filepath.Join(r.dir, "foo.img"), 128, []byte("foo foo foo")) 178 179 ps := &gadget.LaidOutStructure{ 180 VolumeStructure: &gadget.VolumeStructure{ 181 Size: 2048, 182 }, 183 LaidOutContent: []gadget.LaidOutContent{ 184 { 185 VolumeContent: &gadget.VolumeContent{ 186 Image: "foo.img", 187 }, 188 StartOffset: 1024, 189 Size: 128, 190 }, 191 }, 192 } 193 rw, err := gadget.NewRawStructureWriter(r.dir, ps) 194 c.Assert(err, IsNil) 195 c.Assert(rw, NotNil) 196 197 err = rw.Write(out) 198 c.Assert(err, ErrorMatches, "failed to write image .*: cannot write image: failed") 199 200 out = &mockWriteSeeker{ 201 seek: func(offset int64, whence int) (ret int64, err error) { 202 return 0, errors.New("failed") 203 }, 204 } 205 err = rw.Write(out) 206 c.Assert(err, ErrorMatches, "failed to write image .*: cannot seek to content start offset 0x400: failed") 207 } 208 209 func (r *rawTestSuite) TestRawWriterNoImage(c *C) { 210 out := &mockWriteSeeker{} 211 ps := &gadget.LaidOutStructure{ 212 VolumeStructure: &gadget.VolumeStructure{ 213 Size: 2048, 214 }, 215 LaidOutContent: []gadget.LaidOutContent{ 216 { 217 // invalid content 218 VolumeContent: &gadget.VolumeContent{ 219 Image: "", 220 }, 221 StartOffset: 1024, 222 Size: 128, 223 }, 224 }, 225 } 226 rw, err := gadget.NewRawStructureWriter(r.dir, ps) 227 c.Assert(err, IsNil) 228 c.Assert(rw, NotNil) 229 230 err = rw.Write(out) 231 c.Assert(err, ErrorMatches, "failed to write image .*: no image defined") 232 } 233 234 func (r *rawTestSuite) TestRawWriterFailWithNonBare(c *C) { 235 ps := &gadget.LaidOutStructure{ 236 VolumeStructure: &gadget.VolumeStructure{ 237 Size: 2048, 238 // non-bare 239 Filesystem: "ext4", 240 }, 241 } 242 243 rw, err := gadget.NewRawStructureWriter(r.dir, ps) 244 c.Assert(err, ErrorMatches, "internal error: structure #0 is not bare") 245 c.Assert(rw, IsNil) 246 } 247 248 func (r *rawTestSuite) TestRawWriterInternalErrors(c *C) { 249 ps := &gadget.LaidOutStructure{ 250 VolumeStructure: &gadget.VolumeStructure{ 251 Size: 2048, 252 }, 253 } 254 255 rw, err := gadget.NewRawStructureWriter("", ps) 256 c.Assert(err, ErrorMatches, "internal error: gadget content directory cannot be unset") 257 c.Assert(rw, IsNil) 258 259 rw, err = gadget.NewRawStructureWriter(r.dir, nil) 260 c.Assert(err, ErrorMatches, `internal error: \*LaidOutStructure is nil`) 261 c.Assert(rw, IsNil) 262 } 263 264 func getFileSize(c *C, path string) int64 { 265 stat, err := os.Stat(path) 266 c.Assert(err, IsNil) 267 return stat.Size() 268 } 269 270 func (r *rawTestSuite) TestRawUpdaterFailWithNonBare(c *C) { 271 ps := &gadget.LaidOutStructure{ 272 VolumeStructure: &gadget.VolumeStructure{ 273 Size: 2048, 274 // non-bare 275 Filesystem: "ext4", 276 }, 277 } 278 279 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 280 c.Fatalf("unexpected call") 281 return "", 0, nil 282 }) 283 c.Assert(err, ErrorMatches, "internal error: structure #0 is not bare") 284 c.Assert(ru, IsNil) 285 } 286 287 func (r *rawTestSuite) TestRawUpdaterBackupUpdateRestoreSame(c *C) { 288 289 partitionPath := filepath.Join(r.dir, "partition.img") 290 mutateFile(c, partitionPath, 2048, []mutateWrite{ 291 {[]byte("foo foo foo"), 0}, 292 {[]byte("bar bar bar"), 1024}, 293 }) 294 295 makeSizedFile(c, filepath.Join(r.dir, "foo.img"), 128, []byte("foo foo foo")) 296 makeSizedFile(c, filepath.Join(r.dir, "bar.img"), 128, []byte("bar bar bar")) 297 ps := &gadget.LaidOutStructure{ 298 VolumeStructure: &gadget.VolumeStructure{ 299 Size: 2048, 300 }, 301 StartOffset: 1 * gadget.SizeMiB, 302 LaidOutContent: []gadget.LaidOutContent{ 303 { 304 VolumeContent: &gadget.VolumeContent{ 305 Image: "foo.img", 306 }, 307 StartOffset: 1 * gadget.SizeMiB, 308 Size: 128, 309 }, { 310 VolumeContent: &gadget.VolumeContent{ 311 Image: "bar.img", 312 }, 313 StartOffset: 1*gadget.SizeMiB + 1024, 314 Size: 128, 315 Index: 1, 316 }, 317 }, 318 } 319 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 320 c.Check(to, DeepEquals, ps) 321 // Structure has a partition, thus it starts at 0 offset. 322 return partitionPath, 0, nil 323 }) 324 c.Assert(err, IsNil) 325 c.Assert(ru, NotNil) 326 327 err = ru.Backup() 328 c.Assert(err, IsNil) 329 330 c.Check(osutil.FileExists(gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0])+".same"), Equals, true) 331 c.Check(osutil.FileExists(gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[1])+".same"), Equals, true) 332 333 emptyDiskPath := filepath.Join(r.dir, "disk-not-written.img") 334 err = osutil.AtomicWriteFile(emptyDiskPath, nil, 0644, 0) 335 c.Assert(err, IsNil) 336 // update should be a noop now, use the same locations, point to a file 337 // of 0 size, so that seek fails and write would increase the size 338 ru, err = gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 339 return emptyDiskPath, 0, nil 340 }) 341 c.Assert(err, IsNil) 342 c.Assert(ru, NotNil) 343 344 err = ru.Update() 345 c.Assert(err, IsNil) 346 c.Check(getFileSize(c, emptyDiskPath), Equals, int64(0)) 347 348 // rollback also is a noop 349 err = ru.Rollback() 350 c.Assert(err, IsNil) 351 c.Check(getFileSize(c, emptyDiskPath), Equals, int64(0)) 352 } 353 354 func (r *rawTestSuite) TestRawUpdaterBackupUpdateRestoreDifferent(c *C) { 355 356 diskPath := filepath.Join(r.dir, "partition.img") 357 mutateFile(c, diskPath, 2048, []mutateWrite{ 358 {[]byte("foo foo foo"), 0}, 359 {[]byte("bar bar bar"), 1024}, 360 }) 361 362 pristinePath := filepath.Join(r.dir, "pristine.img") 363 err := osutil.CopyFile(diskPath, pristinePath, 0) 364 c.Assert(err, IsNil) 365 366 expectedPath := filepath.Join(r.dir, "expected.img") 367 mutateFile(c, expectedPath, 2048, []mutateWrite{ 368 {[]byte("zzz zzz zzz zzz"), 0}, 369 {[]byte("xxx xxx xxx xxx"), 1024}, 370 }) 371 372 makeSizedFile(c, filepath.Join(r.dir, "foo.img"), 128, []byte("zzz zzz zzz zzz")) 373 makeSizedFile(c, filepath.Join(r.dir, "bar.img"), 256, []byte("xxx xxx xxx xxx")) 374 ps := &gadget.LaidOutStructure{ 375 VolumeStructure: &gadget.VolumeStructure{ 376 Size: 2048, 377 }, 378 StartOffset: 1 * gadget.SizeMiB, 379 LaidOutContent: []gadget.LaidOutContent{ 380 { 381 VolumeContent: &gadget.VolumeContent{ 382 Image: "foo.img", 383 }, 384 StartOffset: 1 * gadget.SizeMiB, 385 Size: 128, 386 }, { 387 VolumeContent: &gadget.VolumeContent{ 388 Image: "bar.img", 389 }, 390 StartOffset: 1*gadget.SizeMiB + 1024, 391 Size: 256, 392 Index: 1, 393 }, 394 }, 395 } 396 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 397 c.Check(to, DeepEquals, ps) 398 // Structure has a partition, thus it starts at 0 offset. 399 return diskPath, 0, nil 400 }) 401 c.Assert(err, IsNil) 402 c.Assert(ru, NotNil) 403 404 err = ru.Backup() 405 c.Assert(err, IsNil) 406 407 for _, e := range []struct { 408 path string 409 size int64 410 exists bool 411 }{ 412 {gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0]) + ".backup", 128, true}, 413 {gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[1]) + ".backup", 256, true}, 414 {gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[1]) + ".same", 0, false}, 415 {gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[1]) + ".same", 0, false}, 416 } { 417 c.Check(osutil.FileExists(e.path), Equals, e.exists) 418 if e.exists { 419 c.Check(getFileSize(c, e.path), Equals, e.size) 420 } 421 } 422 423 err = ru.Update() 424 c.Assert(err, IsNil) 425 426 // after update, files should be identical 427 c.Check(osutil.FilesAreEqual(diskPath, expectedPath), Equals, true) 428 429 // rollback restores the original contents 430 err = ru.Rollback() 431 c.Assert(err, IsNil) 432 433 // which should match the pristine copy now 434 c.Check(osutil.FilesAreEqual(diskPath, pristinePath), Equals, true) 435 } 436 437 func (r *rawTestSuite) TestRawUpdaterBackupUpdateRestoreNoPartition(c *C) { 438 diskPath := filepath.Join(r.dir, "disk.img") 439 440 mutateFile(c, diskPath, gadget.SizeMiB+2048, []mutateWrite{ 441 {[]byte("baz baz baz"), int64(gadget.SizeMiB)}, 442 {[]byte("oof oof oof"), int64(gadget.SizeMiB + 1024)}, 443 }) 444 445 pristinePath := filepath.Join(r.dir, "pristine.img") 446 err := osutil.CopyFile(diskPath, pristinePath, 0) 447 c.Assert(err, IsNil) 448 449 expectedPath := filepath.Join(r.dir, "expected.img") 450 mutateFile(c, expectedPath, gadget.SizeMiB+2048, []mutateWrite{ 451 {[]byte("zzz zzz zzz zzz"), int64(gadget.SizeMiB)}, 452 {[]byte("xxx xxx xxx xxx"), int64(gadget.SizeMiB + 1024)}, 453 }) 454 455 makeSizedFile(c, filepath.Join(r.dir, "foo.img"), 128, []byte("zzz zzz zzz zzz")) 456 makeSizedFile(c, filepath.Join(r.dir, "bar.img"), 256, []byte("xxx xxx xxx xxx")) 457 ps := &gadget.LaidOutStructure{ 458 VolumeStructure: &gadget.VolumeStructure{ 459 // No partition table entry, would trigger fallback lookup path. 460 Type: "bare", 461 Size: 2048, 462 }, 463 StartOffset: 1 * gadget.SizeMiB, 464 LaidOutContent: []gadget.LaidOutContent{ 465 { 466 VolumeContent: &gadget.VolumeContent{ 467 Image: "foo.img", 468 }, 469 StartOffset: 1 * gadget.SizeMiB, 470 Size: 128, 471 }, { 472 VolumeContent: &gadget.VolumeContent{ 473 Image: "bar.img", 474 }, 475 StartOffset: 1*gadget.SizeMiB + 1024, 476 Size: 256, 477 Index: 1, 478 }, 479 }, 480 } 481 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 482 c.Check(to, DeepEquals, ps) 483 // No partition table, returned path corresponds to a disk, start offset is non-0. 484 return diskPath, ps.StartOffset, nil 485 }) 486 c.Assert(err, IsNil) 487 c.Assert(ru, NotNil) 488 489 err = ru.Backup() 490 c.Assert(err, IsNil) 491 492 for _, e := range []struct { 493 path string 494 size int64 495 }{ 496 {gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0]) + ".backup", 128}, 497 {gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[1]) + ".backup", 256}, 498 } { 499 c.Check(osutil.FileExists(e.path), Equals, true) 500 c.Check(getFileSize(c, e.path), Equals, e.size) 501 } 502 503 err = ru.Update() 504 c.Assert(err, IsNil) 505 506 // After update, files should be identical. 507 c.Check(osutil.FilesAreEqual(diskPath, expectedPath), Equals, true) 508 509 // Rollback restores the original contents. 510 err = ru.Rollback() 511 c.Assert(err, IsNil) 512 513 // Which should match the pristine copy now. 514 c.Check(osutil.FilesAreEqual(diskPath, pristinePath), Equals, true) 515 } 516 517 func (r *rawTestSuite) TestRawUpdaterBackupErrors(c *C) { 518 diskPath := filepath.Join(r.dir, "disk.img") 519 ps := &gadget.LaidOutStructure{ 520 VolumeStructure: &gadget.VolumeStructure{ 521 Size: 2048, 522 }, 523 StartOffset: 0, 524 LaidOutContent: []gadget.LaidOutContent{ 525 { 526 VolumeContent: &gadget.VolumeContent{ 527 Image: "foo.img", 528 }, 529 StartOffset: 128, 530 Size: 128, 531 }, 532 }, 533 } 534 535 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 536 c.Check(to, DeepEquals, ps) 537 return diskPath, 0, nil 538 }) 539 c.Assert(err, IsNil) 540 c.Assert(ru, NotNil) 541 542 err = ru.Backup() 543 c.Assert(err, ErrorMatches, "cannot open device for reading: .*") 544 c.Check(osutil.FileExists(gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0])+".backup"), Equals, false) 545 546 // 0 sized disk, copying will fail with early EOF 547 makeSizedFile(c, diskPath, 0, nil) 548 549 err = ru.Backup() 550 c.Assert(err, ErrorMatches, "cannot backup image .*: cannot backup original image: EOF") 551 c.Check(osutil.FileExists(gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0])+".backup"), Equals, false) 552 553 // make proper disk image now 554 err = os.Remove(diskPath) 555 c.Assert(err, IsNil) 556 makeSizedFile(c, diskPath, 2048, nil) 557 558 err = ru.Backup() 559 c.Assert(err, ErrorMatches, "cannot backup image .*: cannot checksum update image: .*") 560 c.Check(osutil.FileExists(gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0])+".backup"), Equals, false) 561 } 562 563 func (r *rawTestSuite) TestRawUpdaterBackupIdempotent(c *C) { 564 diskPath := filepath.Join(r.dir, "disk.img") 565 // 0 sized disk, copying will fail with early EOF 566 makeSizedFile(c, diskPath, 0, nil) 567 568 ps := &gadget.LaidOutStructure{ 569 VolumeStructure: &gadget.VolumeStructure{ 570 Size: 2048, 571 }, 572 StartOffset: 0, 573 LaidOutContent: []gadget.LaidOutContent{ 574 { 575 VolumeContent: &gadget.VolumeContent{ 576 Image: "foo.img", 577 }, 578 StartOffset: 128, 579 Size: 128, 580 }, 581 }, 582 } 583 584 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 585 c.Check(to, DeepEquals, ps) 586 return diskPath, 0, nil 587 }) 588 c.Assert(err, IsNil) 589 c.Assert(ru, NotNil) 590 591 contentBackupBasePath := gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0]) 592 // mock content backed-up marker 593 makeSizedFile(c, contentBackupBasePath+".backup", 0, nil) 594 595 // never reached copy, hence no error 596 err = ru.Backup() 597 c.Assert(err, IsNil) 598 599 err = os.Remove(contentBackupBasePath + ".backup") 600 c.Assert(err, IsNil) 601 602 // mock content is-identical marker 603 makeSizedFile(c, contentBackupBasePath+".same", 0, nil) 604 // never reached copy, hence no error 605 err = ru.Backup() 606 c.Assert(err, IsNil) 607 } 608 609 func (r *rawTestSuite) TestRawUpdaterFindDeviceFailed(c *C) { 610 ps := &gadget.LaidOutStructure{ 611 VolumeStructure: &gadget.VolumeStructure{ 612 Size: 2048, 613 }, 614 StartOffset: 0, 615 LaidOutContent: []gadget.LaidOutContent{ 616 { 617 VolumeContent: &gadget.VolumeContent{ 618 Image: "foo.img", 619 }, 620 StartOffset: 128, 621 Size: 128, 622 }, 623 }, 624 } 625 626 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, nil) 627 c.Assert(err, ErrorMatches, "internal error: device lookup helper must be provided") 628 c.Assert(ru, IsNil) 629 630 ru, err = gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 631 c.Check(to, DeepEquals, ps) 632 return "", 0, errors.New("failed") 633 }) 634 c.Assert(err, IsNil) 635 c.Assert(ru, NotNil) 636 637 err = ru.Backup() 638 c.Assert(err, ErrorMatches, "cannot find device matching structure #0: failed") 639 640 err = ru.Update() 641 c.Assert(err, ErrorMatches, "cannot find device matching structure #0: failed") 642 643 err = ru.Rollback() 644 c.Assert(err, ErrorMatches, "cannot find device matching structure #0: failed") 645 } 646 647 func (r *rawTestSuite) TestRawUpdaterRollbackErrors(c *C) { 648 diskPath := filepath.Join(r.dir, "disk.img") 649 // 0 sized disk, copying will fail with early EOF 650 makeSizedFile(c, diskPath, 0, nil) 651 652 ps := &gadget.LaidOutStructure{ 653 VolumeStructure: &gadget.VolumeStructure{ 654 Size: 2048, 655 }, 656 StartOffset: 0, 657 LaidOutContent: []gadget.LaidOutContent{ 658 { 659 VolumeContent: &gadget.VolumeContent{ 660 Image: "foo.img", 661 }, 662 StartOffset: 128, 663 Size: 128, 664 }, 665 }, 666 } 667 668 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 669 c.Check(to, DeepEquals, ps) 670 return diskPath, 0, nil 671 }) 672 c.Assert(err, IsNil) 673 c.Assert(ru, NotNil) 674 675 err = ru.Rollback() 676 c.Assert(err, ErrorMatches, `cannot rollback image #0 \("foo.img"@0x80\{128\}\): cannot open backup image: .*no such file or directory`) 677 678 contentBackupPath := gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0]) + ".backup" 679 680 // trigger short read 681 makeSizedFile(c, contentBackupPath, 0, nil) 682 683 err = ru.Rollback() 684 c.Assert(err, ErrorMatches, `cannot rollback image #0 \("foo.img"@0x80\{128\}\): cannot restore backup: cannot write image: EOF`) 685 686 // pretend device cannot be opened for writing 687 err = os.Chmod(diskPath, 0000) 688 c.Assert(err, IsNil) 689 err = ru.Rollback() 690 c.Assert(err, ErrorMatches, "cannot open device for writing: .* permission denied") 691 } 692 693 func (r *rawTestSuite) TestRawUpdaterUpdateErrors(c *C) { 694 diskPath := filepath.Join(r.dir, "disk.img") 695 // 0 sized disk, copying will fail with early EOF 696 makeSizedFile(c, diskPath, 2048, nil) 697 698 ps := &gadget.LaidOutStructure{ 699 VolumeStructure: &gadget.VolumeStructure{ 700 Size: 2048, 701 }, 702 StartOffset: 0, 703 LaidOutContent: []gadget.LaidOutContent{ 704 { 705 VolumeContent: &gadget.VolumeContent{ 706 Image: "foo.img", 707 }, 708 StartOffset: 128, 709 Size: 128, 710 }, 711 }, 712 } 713 714 ru, err := gadget.NewRawStructureUpdater(r.dir, ps, r.backup, func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 715 c.Check(to, DeepEquals, ps) 716 return diskPath, 0, nil 717 }) 718 c.Assert(err, IsNil) 719 c.Assert(ru, NotNil) 720 721 // backup/analysis not performed 722 err = ru.Update() 723 c.Assert(err, ErrorMatches, `cannot update image #0 \("foo.img"@0x80\{128\}\): missing backup file`) 724 725 // pretend backup was done 726 makeSizedFile(c, gadget.RawContentBackupPath(r.backup, ps, &ps.LaidOutContent[0])+".backup", 0, nil) 727 728 err = ru.Update() 729 c.Assert(err, ErrorMatches, `cannot update image #0 \("foo.img"@0x80\{128\}\).*: cannot open image file: .*no such file or directory`) 730 731 makeSizedFile(c, filepath.Join(r.dir, "foo.img"), 0, nil) 732 err = ru.Update() 733 c.Assert(err, ErrorMatches, `cannot update image #0 \("foo.img"@0x80\{128\}\).*: cannot write image: EOF`) 734 735 // pretend device cannot be opened for writing 736 err = os.Chmod(diskPath, 0000) 737 c.Assert(err, IsNil) 738 err = ru.Update() 739 c.Assert(err, ErrorMatches, "cannot open device for writing: .* permission denied") 740 } 741 742 func (r *rawTestSuite) TestRawUpdaterContentBackupPath(c *C) { 743 ps := &gadget.LaidOutStructure{ 744 VolumeStructure: &gadget.VolumeStructure{}, 745 StartOffset: 0, 746 LaidOutContent: []gadget.LaidOutContent{ 747 { 748 VolumeContent: &gadget.VolumeContent{}, 749 }, 750 }, 751 } 752 pc := &ps.LaidOutContent[0] 753 754 p := gadget.RawContentBackupPath(r.backup, ps, pc) 755 c.Assert(p, Equals, r.backup+"/struct-0-0") 756 pc.Index = 5 757 p = gadget.RawContentBackupPath(r.backup, ps, pc) 758 c.Assert(p, Equals, r.backup+"/struct-0-5") 759 ps.Index = 9 760 p = gadget.RawContentBackupPath(r.backup, ps, pc) 761 c.Assert(p, Equals, r.backup+"/struct-9-5") 762 } 763 764 func (r *rawTestSuite) TestRawUpdaterInternalErrors(c *C) { 765 ps := &gadget.LaidOutStructure{ 766 VolumeStructure: &gadget.VolumeStructure{ 767 Size: 2048, 768 }, 769 } 770 771 f := func(to *gadget.LaidOutStructure) (string, gadget.Size, error) { 772 return "", 0, errors.New("unexpected call") 773 } 774 rw, err := gadget.NewRawStructureUpdater("", ps, r.backup, f) 775 c.Assert(err, ErrorMatches, "internal error: gadget content directory cannot be unset") 776 c.Assert(rw, IsNil) 777 778 rw, err = gadget.NewRawStructureUpdater(r.dir, nil, r.backup, f) 779 c.Assert(err, ErrorMatches, `internal error: \*LaidOutStructure is nil`) 780 c.Assert(rw, IsNil) 781 782 rw, err = gadget.NewRawStructureUpdater(r.dir, ps, "", f) 783 c.Assert(err, ErrorMatches, "internal error: backup directory cannot be unset") 784 c.Assert(rw, IsNil) 785 786 rw, err = gadget.NewRawStructureUpdater(r.dir, ps, r.backup, nil) 787 c.Assert(err, ErrorMatches, "internal error: device lookup helper must be provided") 788 c.Assert(rw, IsNil) 789 }