github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/gadget/ondisk_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019-2020 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 "io/ioutil" 24 "os" 25 "path/filepath" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/gadget" 30 "github.com/snapcore/snapd/gadget/quantity" 31 "github.com/snapcore/snapd/testutil" 32 ) 33 34 type ondiskTestSuite struct { 35 testutil.BaseTest 36 37 dir string 38 39 gadgetRoot string 40 } 41 42 var _ = Suite(&ondiskTestSuite{}) 43 44 func (s *ondiskTestSuite) SetUpTest(c *C) { 45 s.BaseTest.SetUpTest(c) 46 47 s.dir = c.MkDir() 48 49 s.gadgetRoot = c.MkDir() 50 err := makeMockGadget(s.gadgetRoot, gadgetContent) 51 c.Assert(err, IsNil) 52 } 53 54 const mockSfdiskScriptBiosSeed = ` 55 >&2 echo "Some warning from sfdisk" 56 echo '{ 57 "partitiontable": { 58 "label": "gpt", 59 "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA", 60 "device": "/dev/node", 61 "unit": "sectors", 62 "firstlba": 34, 63 "lastlba": 8388574, 64 "partitions": [ 65 { 66 "node": "/dev/node1", 67 "start": 2048, 68 "size": 2048, 69 "type": "21686148-6449-6E6F-744E-656564454649", 70 "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F", 71 "name": "BIOS Boot" 72 }, 73 { 74 "node": "/dev/node2", 75 "start": 4096, 76 "size": 2457600, 77 "type": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", 78 "uuid": "44C3D5C3-CAE1-4306-83E8-DF437ACDB32F", 79 "name": "Recovery" 80 } 81 ] 82 } 83 }'` 84 85 const mockLsblkScriptBiosSeed = ` 86 [ "$3" == "/dev/node1" ] && echo '{ 87 "blockdevices": [ {"name": "node1", "fstype": null, "label": null, "uuid": null, "mountpoint": null} ] 88 }' 89 [ "$3" == "/dev/node2" ] && echo '{ 90 "blockdevices": [ {"name": "node2", "fstype": "vfat", "label": "ubuntu-seed", "uuid": "A644-B807", "mountpoint": null} ] 91 }' 92 exit 0` 93 94 func makeMockGadget(gadgetRoot, gadgetContent string) error { 95 if err := os.MkdirAll(filepath.Join(gadgetRoot, "meta"), 0755); err != nil { 96 return err 97 } 98 if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "meta", "gadget.yaml"), []byte(gadgetContent), 0644); err != nil { 99 return err 100 } 101 if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "pc-boot.img"), []byte("pc-boot.img content"), 0644); err != nil { 102 return err 103 } 104 if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "pc-core.img"), []byte("pc-core.img content"), 0644); err != nil { 105 return err 106 } 107 if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "grubx64.efi"), []byte("grubx64.efi content"), 0644); err != nil { 108 return err 109 } 110 111 return nil 112 } 113 114 const gadgetContent = `volumes: 115 pc: 116 bootloader: grub 117 structure: 118 - name: mbr 119 type: mbr 120 size: 440 121 content: 122 - image: pc-boot.img 123 - name: BIOS Boot 124 type: DA,21686148-6449-6E6F-744E-656564454649 125 size: 1M 126 offset: 1M 127 offset-write: mbr+92 128 content: 129 - image: pc-core.img 130 - name: Recovery 131 role: system-seed 132 filesystem: vfat 133 # UEFI will boot the ESP partition by default first 134 type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B 135 size: 1200M 136 content: 137 - source: grubx64.efi 138 target: EFI/boot/grubx64.efi 139 - name: Save 140 role: system-save 141 filesystem: ext4 142 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 143 size: 128M 144 - name: Writable 145 role: system-data 146 filesystem: ext4 147 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 148 size: 1200M 149 ` 150 151 func (s *ondiskTestSuite) TestDeviceInfoGPT(c *C) { 152 cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskScriptBiosSeed) 153 defer cmdSfdisk.Restore() 154 155 cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkScriptBiosSeed) 156 defer cmdLsblk.Restore() 157 158 dl, err := gadget.OnDiskVolumeFromDevice("/dev/node") 159 c.Assert(err, IsNil) 160 c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{ 161 {"sfdisk", "--json", "/dev/node"}, 162 }) 163 c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{ 164 {"lsblk", "--fs", "--json", "/dev/node1"}, 165 {"lsblk", "--fs", "--json", "/dev/node2"}, 166 }) 167 c.Assert(err, IsNil) 168 c.Assert(dl.Schema, Equals, "gpt") 169 c.Assert(dl.ID, Equals, "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA") 170 c.Assert(dl.Device, Equals, "/dev/node") 171 c.Assert(dl.SectorSize, Equals, quantity.Size(512)) 172 c.Assert(dl.Size, Equals, quantity.Size(8388575*512)) 173 c.Assert(len(dl.Structure), Equals, 2) 174 175 c.Assert(dl.Structure, DeepEquals, []gadget.OnDiskStructure{ 176 { 177 LaidOutStructure: gadget.LaidOutStructure{ 178 VolumeStructure: &gadget.VolumeStructure{ 179 Name: "BIOS Boot", 180 Size: 0x100000, 181 Label: "", 182 Type: "21686148-6449-6E6F-744E-656564454649", 183 Filesystem: "", 184 }, 185 StartOffset: 0x100000, 186 Index: 1, 187 }, 188 Node: "/dev/node1", 189 }, 190 { 191 LaidOutStructure: gadget.LaidOutStructure{ 192 VolumeStructure: &gadget.VolumeStructure{ 193 Name: "Recovery", 194 Size: 0x4b000000, 195 Label: "ubuntu-seed", 196 Type: "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", 197 Filesystem: "vfat", 198 }, 199 StartOffset: 0x200000, 200 Index: 2, 201 }, 202 Node: "/dev/node2", 203 }, 204 }) 205 } 206 207 func (s *ondiskTestSuite) TestDeviceInfoMBR(c *C) { 208 const mockSfdiskWithMBR = ` 209 >&2 echo "Some warning from sfdisk" 210 echo '{ 211 "partitiontable": { 212 "label": "dos", 213 "device": "/dev/node", 214 "unit": "sectors", 215 "partitions": [ 216 {"node": "/dev/node1", "start": 4096, "size": 2457600, "type": "c"}, 217 {"node": "/dev/node2", "start": 2461696, "size": 1048576, "type": "d"}, 218 {"node": "/dev/node3", "start": 3510272, "size": 1048576, "type": "d"}, 219 {"node": "/dev/node4", "start": 4558848, "size": 1048576, "type": "d"} 220 ] 221 } 222 }'` 223 const mockLsblkForMBR = ` 224 [ "$3" == "/dev/node1" ] && echo '{ 225 "blockdevices": [ {"name": "node1", "fstype": "vfat", "label": "ubuntu-seed", "uuid": "A644-B807", "mountpoint": null} ] 226 }' 227 [ "$3" == "/dev/node2" ] && echo '{ 228 "blockdevices": [ {"name": "node2", "fstype": "vfat", "label": "ubuntu-boot", "uuid": "A644-B808", "mountpoint": null} ] 229 }' 230 [ "$3" == "/dev/node3" ] && echo '{ 231 "blockdevices": [ {"name": "node3", "fstype": "ext4", "label": "ubuntu-save", "mountpoint": null} ] 232 }' 233 [ "$3" == "/dev/node4" ] && echo '{ 234 "blockdevices": [ {"name": "node3", "fstype": "ext4", "label": "ubuntu-data", "mountpoint": null} ] 235 }' 236 exit 0` 237 238 cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskWithMBR) 239 defer cmdSfdisk.Restore() 240 241 cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkForMBR) 242 defer cmdLsblk.Restore() 243 244 cmdBlockdev := testutil.MockCommand(c, "blockdev", "echo 12345670") 245 defer cmdBlockdev.Restore() 246 247 dl, err := gadget.OnDiskVolumeFromDevice("/dev/node") 248 c.Assert(err, IsNil) 249 c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{ 250 {"sfdisk", "--json", "/dev/node"}, 251 }) 252 c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{ 253 {"lsblk", "--fs", "--json", "/dev/node1"}, 254 {"lsblk", "--fs", "--json", "/dev/node2"}, 255 {"lsblk", "--fs", "--json", "/dev/node3"}, 256 {"lsblk", "--fs", "--json", "/dev/node4"}, 257 }) 258 c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{ 259 {"blockdev", "--getsz", "/dev/node"}, 260 }) 261 c.Assert(err, IsNil) 262 c.Assert(dl.ID, Equals, "") 263 c.Assert(dl.Schema, Equals, "dos") 264 c.Assert(dl.Device, Equals, "/dev/node") 265 c.Assert(dl.SectorSize, Equals, quantity.Size(512)) 266 c.Assert(dl.Size, Equals, quantity.Size(12345670*512)) 267 c.Assert(len(dl.Structure), Equals, 4) 268 269 c.Assert(dl.Structure, DeepEquals, []gadget.OnDiskStructure{ 270 { 271 LaidOutStructure: gadget.LaidOutStructure{ 272 VolumeStructure: &gadget.VolumeStructure{ 273 Size: 0x4b000000, 274 Label: "ubuntu-seed", 275 Type: "0C", 276 Filesystem: "vfat", 277 }, 278 StartOffset: 0x200000, 279 Index: 1, 280 }, 281 Node: "/dev/node1", 282 }, 283 { 284 LaidOutStructure: gadget.LaidOutStructure{ 285 VolumeStructure: &gadget.VolumeStructure{ 286 Size: 0x20000000, 287 Label: "ubuntu-boot", 288 Type: "0D", 289 Filesystem: "vfat", 290 }, 291 StartOffset: 0x4b200000, 292 Index: 2, 293 }, 294 Node: "/dev/node2", 295 }, 296 { 297 LaidOutStructure: gadget.LaidOutStructure{ 298 VolumeStructure: &gadget.VolumeStructure{ 299 Size: 0x20000000, 300 Label: "ubuntu-save", 301 Type: "0D", 302 Filesystem: "ext4", 303 }, 304 StartOffset: 0x6b200000, 305 Index: 3, 306 }, 307 Node: "/dev/node3", 308 }, 309 { 310 LaidOutStructure: gadget.LaidOutStructure{ 311 VolumeStructure: &gadget.VolumeStructure{ 312 Size: 0x20000000, 313 Label: "ubuntu-data", 314 Type: "0D", 315 Filesystem: "ext4", 316 }, 317 StartOffset: 0x8b200000, 318 Index: 4, 319 }, 320 Node: "/dev/node4", 321 }, 322 }) 323 } 324 325 func (s *ondiskTestSuite) TestDeviceInfoNotSectors(c *C) { 326 cmdSfdisk := testutil.MockCommand(c, "sfdisk", `echo '{ 327 "partitiontable": { 328 "label": "gpt", 329 "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA", 330 "device": "/dev/node", 331 "unit": "not_sectors", 332 "firstlba": 34, 333 "lastlba": 8388574, 334 "partitions": [ 335 {"node": "/dev/node1", "start": 2048, "size": 2048, "type": "21686148-6449-6E6F-744E-656564454649", "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F", "name": "BIOS Boot"} 336 ] 337 } 338 }'`) 339 defer cmdSfdisk.Restore() 340 341 _, err := gadget.OnDiskVolumeFromDevice("/dev/node") 342 c.Assert(err, ErrorMatches, "cannot position partitions: unknown unit .*") 343 } 344 345 func (s *ondiskTestSuite) TestDeviceInfoFilesystemInfoError(c *C) { 346 cmdSfdisk := testutil.MockCommand(c, "sfdisk", `echo '{ 347 "partitiontable": { 348 "label": "gpt", 349 "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA", 350 "device": "/dev/node", 351 "unit": "sectors", 352 "firstlba": 34, 353 "lastlba": 8388574, 354 "partitions": [ 355 {"node": "/dev/node1", "start": 2048, "size": 2048, "type": "21686148-6449-6E6F-744E-656564454649", "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F", "name": "BIOS Boot"} 356 ] 357 } 358 }'`) 359 defer cmdSfdisk.Restore() 360 361 cmdLsblk := testutil.MockCommand(c, "lsblk", "echo lsblk error; exit 1") 362 defer cmdLsblk.Restore() 363 364 _, err := gadget.OnDiskVolumeFromDevice("/dev/node") 365 c.Assert(err, ErrorMatches, "cannot obtain filesystem information: lsblk error") 366 } 367 368 func (s *ondiskTestSuite) TestDeviceInfoJsonError(c *C) { 369 cmd := testutil.MockCommand(c, "sfdisk", `echo 'This is not a json'`) 370 defer cmd.Restore() 371 372 dl, err := gadget.OnDiskVolumeFromDevice("/dev/node") 373 c.Assert(err, ErrorMatches, "cannot parse sfdisk output: invalid .*") 374 c.Assert(dl, IsNil) 375 } 376 377 func (s *ondiskTestSuite) TestDeviceInfoError(c *C) { 378 cmd := testutil.MockCommand(c, "sfdisk", "echo 'sfdisk: not found'; exit 127") 379 defer cmd.Restore() 380 381 dl, err := gadget.OnDiskVolumeFromDevice("/dev/node") 382 c.Assert(err, ErrorMatches, "sfdisk: not found") 383 c.Assert(dl, IsNil) 384 } 385 386 func (s *ondiskTestSuite) TestUpdatePartitionList(c *C) { 387 const mockSfdiskScriptBios = ` 388 >&2 echo "Some warning from sfdisk" 389 echo '{ 390 "partitiontable": { 391 "label": "gpt", 392 "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA", 393 "device": "/dev/node", 394 "unit": "sectors", 395 "firstlba": 34, 396 "lastlba": 8388574, 397 "partitions": [ 398 { 399 "node": "/dev/node1", 400 "start": 2048, 401 "size": 2048, 402 "type": "21686148-6449-6E6F-744E-656564454649", 403 "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F", 404 "name": "BIOS Boot" 405 } 406 ] 407 } 408 }'` 409 410 const mockLsblkScriptBios = ` 411 [ "$3" == "/dev/node1" ] && echo '{ 412 "blockdevices": [ {"name": "node1", "fstype": null, "label": null, "uuid": null, "mountpoint": null} ] 413 }' 414 exit 0` 415 416 // start with a single partition 417 cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskScriptBios) 418 defer cmdSfdisk.Restore() 419 420 cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkScriptBios) 421 defer cmdLsblk.Restore() 422 423 dl, err := gadget.OnDiskVolumeFromDevice("/dev/node") 424 c.Assert(err, IsNil) 425 c.Assert(len(dl.Structure), Equals, 1) 426 c.Assert(dl.Structure[0].Node, Equals, "/dev/node1") 427 428 // add a partition 429 cmdSfdisk = testutil.MockCommand(c, "sfdisk", mockSfdiskScriptBiosSeed) 430 defer cmdSfdisk.Restore() 431 432 cmdLsblk = testutil.MockCommand(c, "lsblk", mockLsblkScriptBiosSeed) 433 defer cmdLsblk.Restore() 434 435 // update the partition list 436 err = gadget.UpdatePartitionList(dl) 437 c.Assert(err, IsNil) 438 439 // check if the partition list was updated 440 c.Assert(len(dl.Structure), Equals, 2) 441 c.Assert(dl.Structure[0].Node, Equals, "/dev/node1") 442 c.Assert(dl.Structure[1].Node, Equals, "/dev/node2") 443 } 444 445 func (s *ondiskTestSuite) TestFilesystemInfo(c *C) { 446 cmd := testutil.MockCommand(c, "lsblk", `echo '{ 447 "blockdevices": [ 448 {"name": "loop8p2", "fstype": "vfat", "label": "ubuntu-seed", "uuid": "C1F4-CE43", "mountpoint": null} 449 ] 450 }'`) 451 defer cmd.Restore() 452 453 info, err := gadget.FilesystemInfo("/dev/node") 454 c.Assert(cmd.Calls(), DeepEquals, [][]string{ 455 {"lsblk", "--fs", "--json", "/dev/node"}, 456 }) 457 c.Assert(err, IsNil) 458 c.Assert(len(info.BlockDevices), Equals, 1) 459 bd := info.BlockDevices[0] 460 c.Assert(bd.Name, Equals, "loop8p2") 461 c.Assert(bd.FSType, Equals, "vfat") 462 c.Assert(bd.Label, Equals, "ubuntu-seed") 463 c.Assert(bd.UUID, Equals, "C1F4-CE43") 464 } 465 466 func (s *ondiskTestSuite) TestFilesystemInfoJsonError(c *C) { 467 cmd := testutil.MockCommand(c, "lsblk", `echo 'This is not a json'`) 468 defer cmd.Restore() 469 470 info, err := gadget.FilesystemInfo("/dev/node") 471 c.Assert(err, ErrorMatches, "cannot parse lsblk output: invalid .*") 472 c.Assert(info, IsNil) 473 } 474 475 func (s *ondiskTestSuite) TestFilesystemInfoError(c *C) { 476 cmd := testutil.MockCommand(c, "lsblk", "echo 'lsblk: not found'; exit 127") 477 defer cmd.Restore() 478 479 info, err := gadget.FilesystemInfo("/dev/node") 480 c.Assert(err, ErrorMatches, "lsblk: not found") 481 c.Assert(info, IsNil) 482 }