github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/disks/disks_linux_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 disks_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/dirs" 31 "github.com/snapcore/snapd/osutil" 32 "github.com/snapcore/snapd/osutil/disks" 33 "github.com/snapcore/snapd/testutil" 34 ) 35 36 var ( 37 virtioDiskDevPath = "/devices/pci0000:00/0000:00:03.0/virtio1/block/vda/" 38 39 // typical real-world values for tests 40 diskUdevPropMap = map[string]string{ 41 "ID_PART_ENTRY_DISK": "42:0", 42 "DEVNAME": "/dev/vda", 43 "DEVPATH": virtioDiskDevPath, 44 } 45 46 biosBootUdevPropMap = map[string]string{ 47 "ID_PART_ENTRY_UUID": "bios-boot-partuuid", 48 // the udev prop for bios-boot has no fs label, which is typical of the 49 // real bios-boot partition on a amd64 pc gadget system, and so we should 50 // safely just ignore and skip this partition in the fs label 51 // implementation 52 "ID_FS_LABEL_ENC": "", 53 // we will however still have a partition label of "BIOS Boot" 54 "ID_PART_ENTRY_NAME": "BIOS\\x20Boot", 55 } 56 57 // all the ubuntu- partitions have fs labels 58 ubuntuSeedUdevPropMap = map[string]string{ 59 "ID_PART_ENTRY_UUID": "ubuntu-seed-partuuid", 60 "ID_FS_LABEL_ENC": "ubuntu-seed", 61 "ID_PART_ENTRY_NAME": "ubuntu-seed", 62 } 63 ubuntuBootUdevPropMap = map[string]string{ 64 "ID_PART_ENTRY_UUID": "ubuntu-boot-partuuid", 65 "ID_FS_LABEL_ENC": "ubuntu-boot", 66 "ID_PART_ENTRY_NAME": "ubuntu-boot", 67 } 68 ubuntuDataUdevPropMap = map[string]string{ 69 "ID_PART_ENTRY_UUID": "ubuntu-data-partuuid", 70 "ID_FS_LABEL_ENC": "ubuntu-data", 71 "ID_PART_ENTRY_NAME": "ubuntu-data", 72 } 73 ) 74 75 func createVirtioDevicesInSysfs(c *C, devsToPartition map[string]bool) { 76 diskDir := filepath.Join(dirs.SysfsDir, virtioDiskDevPath) 77 for dev, isPartition := range devsToPartition { 78 err := os.MkdirAll(filepath.Join(diskDir, dev), 0755) 79 c.Assert(err, IsNil) 80 if isPartition { 81 err = ioutil.WriteFile(filepath.Join(diskDir, dev, "partition"), []byte("1"), 0644) 82 c.Assert(err, IsNil) 83 } 84 } 85 } 86 87 type diskSuite struct { 88 testutil.BaseTest 89 } 90 91 var _ = Suite(&diskSuite{}) 92 93 func (s *diskSuite) SetUpTest(c *C) { 94 dirs.SetRootDir(c.MkDir()) 95 } 96 97 func (s *diskSuite) TestDiskFromNameHappy(c *C) { 98 restore := disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 99 c.Assert(dev, Equals, "sda") 100 return map[string]string{ 101 "MAJOR": "1", 102 "MINOR": "2", 103 "DEVTYPE": "disk", 104 }, nil 105 }) 106 defer restore() 107 108 d, err := disks.DiskFromDeviceName("sda") 109 c.Assert(err, IsNil) 110 c.Assert(d.Dev(), Equals, "1:2") 111 } 112 113 func (s *diskSuite) TestDiskFromNameUnhappyPartition(c *C) { 114 restore := disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 115 c.Assert(dev, Equals, "sda1") 116 return map[string]string{ 117 "MAJOR": "1", 118 "MINOR": "3", 119 "DEVTYPE": "partition", 120 }, nil 121 }) 122 defer restore() 123 124 _, err := disks.DiskFromDeviceName("sda1") 125 c.Assert(err, ErrorMatches, "device \"sda1\" is not a disk, it has DEVTYPE of \"partition\"") 126 } 127 128 func (s *diskSuite) TestDiskFromNameUnhappyBadUdevOutput(c *C) { 129 restore := disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 130 c.Assert(dev, Equals, "sda") 131 // udev should always return the major/minor but if it doesn't we should 132 // fail 133 return map[string]string{ 134 "MAJOR": "blah blah blah", 135 }, nil 136 }) 137 defer restore() 138 139 _, err := disks.DiskFromDeviceName("sda") 140 c.Assert(err, ErrorMatches, "cannot find disk with name \"sda\": malformed udev output") 141 } 142 143 func (s *diskSuite) TestDiskFromMountPointUnhappyMissingMountpoint(c *C) { 144 // no mount points 145 restore := osutil.MockMountInfo(``) 146 defer restore() 147 148 _, err := disks.DiskFromMountPoint("/run/mnt/blah", nil) 149 c.Assert(err, ErrorMatches, "cannot find mountpoint \"/run/mnt/blah\"") 150 } 151 152 func (s *diskSuite) TestDiskFromMountPointUnhappyMissingUdevProps(c *C) { 153 restore := osutil.MockMountInfo(`130 30 42:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/vda4 rw 154 `) 155 defer restore() 156 157 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 158 c.Assert(dev, Equals, "/dev/vda4") 159 return map[string]string{ 160 "prop": "hello", 161 }, nil 162 }) 163 defer restore() 164 165 _, err := disks.DiskFromMountPoint("/run/mnt/point", nil) 166 c.Assert(err, ErrorMatches, "cannot find disk for partition /dev/vda4, incomplete udev output") 167 } 168 169 func (s *diskSuite) TestDiskFromMountPointUnhappyBadUdevPropsMountpointPartition(c *C) { 170 restore := osutil.MockMountInfo(`130 30 42:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/vda4 rw 171 `) 172 defer restore() 173 174 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 175 c.Assert(dev, Equals, "/dev/vda4") 176 return map[string]string{ 177 "ID_PART_ENTRY_DISK": "not-a-number", 178 }, nil 179 }) 180 defer restore() 181 182 _, err := disks.DiskFromMountPoint("/run/mnt/point", nil) 183 c.Assert(err, ErrorMatches, `cannot find disk for partition /dev/vda4, bad udev output: invalid device number format: \(expected <int>:<int>\)`) 184 } 185 186 func (s *diskSuite) TestDiskFromMountPointUnhappyIsDecryptedDeviceNotDiskDevice(c *C) { 187 restore := osutil.MockMountInfo(`130 30 42:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/vda4 rw 188 `) 189 defer restore() 190 191 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 192 switch dev { 193 case "/dev/vda4": 194 return map[string]string{ 195 "ID_PART_ENTRY_DISK": "42:0", 196 // DEVTYPE == partition is unexpected for this, so this makes 197 // DiskFromMountPoint fail, as decrypted devices should not be 198 // direct partitions, they should be mapper device volumes/disks 199 "DEVTYPE": "partition", 200 }, nil 201 default: 202 c.Errorf("unexpected udev device properties requested: %s", dev) 203 return nil, fmt.Errorf("unexpected udev device: %s", dev) 204 } 205 }) 206 defer restore() 207 208 opts := &disks.Options{IsDecryptedDevice: true} 209 _, err := disks.DiskFromMountPoint("/run/mnt/point", opts) 210 c.Assert(err, ErrorMatches, `mountpoint source /dev/vda4 is not a decrypted device: devtype is not disk \(is partition\)`) 211 } 212 213 func (s *diskSuite) TestDiskFromMountPointUnhappyIsDecryptedDeviceNoSysfs(c *C) { 214 restore := osutil.MockMountInfo(`130 30 252:0 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/mapper/something rw 215 `) 216 defer restore() 217 218 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 219 switch dev { 220 case "/dev/mapper/something": 221 return map[string]string{ 222 "DEVTYPE": "disk", 223 }, nil 224 default: 225 c.Errorf("unexpected udev device properties requested: %s", dev) 226 return nil, fmt.Errorf("unexpected udev device: %s", dev) 227 } 228 }) 229 defer restore() 230 231 // no sysfs files mocking 232 233 opts := &disks.Options{IsDecryptedDevice: true} 234 _, err := disks.DiskFromMountPoint("/run/mnt/point", opts) 235 c.Assert(err, ErrorMatches, fmt.Sprintf(`mountpoint source /dev/mapper/something is not a decrypted device: could not read device mapper metadata: open %s/dev/block/252:0/dm/uuid: no such file or directory`, dirs.SysfsDir)) 236 } 237 238 func (s *diskSuite) TestDiskFromMountPointHappySinglePartitionIgnoresNonPartitionsInSysfs(c *C) { 239 restore := osutil.MockMountInfo(`130 30 47:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/vda4 rw 240 `) 241 defer restore() 242 243 // mock just the single partition and the disk itself in udev 244 n := 0 245 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 246 n++ 247 switch n { 248 case 1, 4: 249 c.Assert(dev, Equals, "/dev/vda4") 250 // this is the partition that was mounted that we initially inspect 251 // to get the disk 252 // this is also called again when we call MountPointIsFromDisk to 253 // verify that the /run/mnt/point is from the same disk 254 return map[string]string{ 255 "ID_PART_ENTRY_DISK": "42:0", 256 }, nil 257 case 2: 258 c.Assert(dev, Equals, "/dev/block/42:0") 259 // this is the disk itself, from ID_PART_ENTRY_DISK above 260 // note that the major/minor for the disk is not adjacent/related to 261 // the partition itself 262 return map[string]string{ 263 "DEVNAME": "/dev/vda", 264 "DEVPATH": virtioDiskDevPath, 265 }, nil 266 case 3: 267 c.Assert(dev, Equals, "vda4") 268 // this is the sysfs entry for the partition of the disk previously 269 // found under the DEVPATH for /dev/block/42:0 270 // this is essentially the same as /dev/block/42:1 in actuality, but 271 // we search for it differently 272 return map[string]string{ 273 "ID_FS_LABEL_ENC": "some-label", 274 "ID_PART_ENTRY_UUID": "some-uuid", 275 }, nil 276 default: 277 c.Errorf("unexpected udev device properties requested: %s", dev) 278 return nil, fmt.Errorf("unexpected udev device: %s", dev) 279 } 280 }) 281 defer restore() 282 283 // create just the single valid partition in sysfs, and an invalid 284 // non-partition device that we should ignore 285 createVirtioDevicesInSysfs(c, map[string]bool{ 286 "vda4": true, 287 "vda5": false, 288 }) 289 290 disk, err := disks.DiskFromMountPoint("/run/mnt/point", nil) 291 c.Assert(err, IsNil) 292 c.Assert(disk.Dev(), Equals, "42:0") 293 c.Assert(disk.HasPartitions(), Equals, true) 294 // searching for the single label we have for this partition will succeed 295 label, err := disk.FindMatchingPartitionUUIDWithFsLabel("some-label") 296 c.Assert(err, IsNil) 297 c.Assert(label, Equals, "some-uuid") 298 299 matches, err := disk.MountPointIsFromDisk("/run/mnt/point", nil) 300 c.Assert(err, IsNil) 301 c.Assert(matches, Equals, true) 302 303 // trying to search for any other labels though will fail 304 _, err = disk.FindMatchingPartitionUUIDWithFsLabel("ubuntu-boot") 305 c.Assert(err, ErrorMatches, "filesystem label \"ubuntu-boot\" not found") 306 c.Assert(err, DeepEquals, disks.PartitionNotFoundError{ 307 SearchType: "filesystem-label", 308 SearchQuery: "ubuntu-boot", 309 }) 310 } 311 312 func (s *diskSuite) TestDiskFromMountPointHappyRealUdevadm(c *C) { 313 restore := osutil.MockMountInfo(`130 30 42:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/vda1 rw 314 `) 315 defer restore() 316 317 udevadmCmd := testutil.MockCommand(c, "udevadm", ` 318 if [ "$*" = "info --query property --name /dev/vda1" ]; then 319 echo "ID_PART_ENTRY_DISK=42:0" 320 else 321 echo "unexpected arguments" 322 exit 1 323 fi 324 `) 325 326 d, err := disks.DiskFromMountPoint("/run/mnt/point", nil) 327 c.Assert(err, IsNil) 328 c.Assert(d.Dev(), Equals, "42:0") 329 c.Assert(d.HasPartitions(), Equals, true) 330 331 c.Assert(udevadmCmd.Calls(), DeepEquals, [][]string{ 332 {"udevadm", "info", "--query", "property", "--name", "/dev/vda1"}, 333 }) 334 } 335 336 func (s *diskSuite) TestDiskFromMountPointVolumeHappy(c *C) { 337 restore := osutil.MockMountInfo(`130 30 42:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/mapper/something rw 338 `) 339 defer restore() 340 341 udevadmCmd := testutil.MockCommand(c, "udevadm", ` 342 if [ "$*" = "info --query property --name /dev/mapper/something" ]; then 343 # not a partition, so no ID_PART_ENTRY_DISK, but we will have DEVTYPE=disk 344 echo "DEVTYPE=disk" 345 else 346 echo "unexpected arguments" 347 exit 1 348 fi 349 `) 350 351 d, err := disks.DiskFromMountPoint("/run/mnt/point", nil) 352 c.Assert(err, IsNil) 353 c.Assert(d.Dev(), Equals, "42:1") 354 c.Assert(d.HasPartitions(), Equals, false) 355 356 c.Assert(udevadmCmd.Calls(), DeepEquals, [][]string{ 357 {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/something"}, 358 }) 359 } 360 361 func (s *diskSuite) TestDiskFromMountPointIsDecryptedDeviceVolumeHappy(c *C) { 362 restore := osutil.MockMountInfo(`130 30 242:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/mapper/something rw 363 `) 364 defer restore() 365 366 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 367 switch dev { 368 case "/dev/mapper/something": 369 return map[string]string{ 370 "DEVTYPE": "disk", 371 }, nil 372 case "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304": 373 return map[string]string{ 374 "DEVTYPE": "disk", 375 }, nil 376 default: 377 c.Errorf("unexpected udev device properties requested: %s", dev) 378 return nil, fmt.Errorf("unexpected udev device: %s", dev) 379 } 380 }) 381 defer restore() 382 383 // mock the sysfs dm uuid and name files 384 dmDir := filepath.Join(filepath.Join(dirs.SysfsDir, "dev", "block"), "242:1", "dm") 385 err := os.MkdirAll(dmDir, 0755) 386 c.Assert(err, IsNil) 387 388 b := []byte("something") 389 err = ioutil.WriteFile(filepath.Join(dmDir, "name"), b, 0644) 390 c.Assert(err, IsNil) 391 392 b = []byte("CRYPT-LUKS2-5a522809c87e4dfa81a88dc5667d1304-something") 393 err = ioutil.WriteFile(filepath.Join(dmDir, "uuid"), b, 0644) 394 c.Assert(err, IsNil) 395 396 opts := &disks.Options{IsDecryptedDevice: true} 397 d, err := disks.DiskFromMountPoint("/run/mnt/point", opts) 398 c.Assert(err, IsNil) 399 c.Assert(d.Dev(), Equals, "242:1") 400 c.Assert(d.HasPartitions(), Equals, false) 401 } 402 403 func (s *diskSuite) TestDiskFromMountPointNotDiskUnsupported(c *C) { 404 restore := osutil.MockMountInfo(`130 30 42:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/not-a-disk rw 405 `) 406 defer restore() 407 408 udevadmCmd := testutil.MockCommand(c, "udevadm", ` 409 if [ "$*" = "info --query property --name /dev/not-a-disk" ]; then 410 echo "DEVTYPE=not-a-disk" 411 else 412 echo "unexpected arguments" 413 exit 1 414 fi 415 `) 416 417 _, err := disks.DiskFromMountPoint("/run/mnt/point", nil) 418 c.Assert(err, ErrorMatches, "unsupported DEVTYPE \"not-a-disk\" for mount point source /dev/not-a-disk") 419 420 c.Assert(udevadmCmd.Calls(), DeepEquals, [][]string{ 421 {"udevadm", "info", "--query", "property", "--name", "/dev/not-a-disk"}, 422 }) 423 } 424 425 func (s *diskSuite) TestDiskFromMountPointPartitionsHappy(c *C) { 426 restore := osutil.MockMountInfo(`130 30 42:4 / /run/mnt/data rw,relatime shared:54 - ext4 /dev/vda4 rw 427 130 30 42:4 / /run/mnt/ubuntu-boot rw,relatime shared:54 - ext4 /dev/vda3 rw 428 `) 429 defer restore() 430 431 n := 0 432 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 433 n++ 434 switch n { 435 case 1: 436 // first request is to the mount point source 437 c.Assert(dev, Equals, "/dev/vda4") 438 return diskUdevPropMap, nil 439 case 2: 440 // next request is for the disk itself 441 c.Assert(dev, Equals, "/dev/block/42:0") 442 return diskUdevPropMap, nil 443 case 3: 444 c.Assert(dev, Equals, "vda1") 445 // this is the sysfs entry for the first partition of the disk 446 // previously found under the DEVPATH for /dev/block/42:0 447 return biosBootUdevPropMap, nil 448 case 4: 449 c.Assert(dev, Equals, "vda2") 450 // the second partition of the disk from sysfs has a fs label 451 return ubuntuSeedUdevPropMap, nil 452 case 5: 453 c.Assert(dev, Equals, "vda3") 454 // same for the third partition 455 return ubuntuBootUdevPropMap, nil 456 case 6: 457 c.Assert(dev, Equals, "vda4") 458 // same for the fourth partition 459 return ubuntuDataUdevPropMap, nil 460 case 7: 461 // next request is for the MountPointIsFromDisk for ubuntu-boot in 462 // this test 463 c.Assert(dev, Equals, "/dev/vda3") 464 return diskUdevPropMap, nil 465 case 8: 466 // next request is for the another DiskFromMountPoint build set of methods we 467 // call in this test 468 c.Assert(dev, Equals, "/dev/vda3") 469 return diskUdevPropMap, nil 470 case 9: 471 // same as for case 2, the disk itself using the major/minor 472 c.Assert(dev, Equals, "/dev/block/42:0") 473 return diskUdevPropMap, nil 474 case 10: 475 c.Assert(dev, Equals, "vda1") 476 // this is the sysfs entry for the first partition of the disk 477 // previously found under the DEVPATH for /dev/block/42:0 478 return biosBootUdevPropMap, nil 479 case 11: 480 c.Assert(dev, Equals, "vda2") 481 // the second partition of the disk from sysfs has a fs label 482 return ubuntuSeedUdevPropMap, nil 483 case 12: 484 c.Assert(dev, Equals, "vda3") 485 // same for the third partition 486 return ubuntuBootUdevPropMap, nil 487 case 13: 488 c.Assert(dev, Equals, "vda4") 489 // same for the fourth partition 490 return ubuntuDataUdevPropMap, nil 491 case 14: 492 // next request is for the MountPointIsFromDisk for ubuntu-data 493 c.Assert(dev, Equals, "/dev/vda4") 494 return diskUdevPropMap, nil 495 default: 496 c.Errorf("unexpected udev device properties requested (request %d): %s", n, dev) 497 return nil, fmt.Errorf("unexpected udev device (request %d): %s", n, dev) 498 } 499 }) 500 defer restore() 501 502 // create all 4 partitions as device nodes in sysfs 503 createVirtioDevicesInSysfs(c, map[string]bool{ 504 "vda1": true, 505 "vda2": true, 506 "vda3": true, 507 "vda4": true, 508 }) 509 510 ubuntuDataDisk, err := disks.DiskFromMountPoint("/run/mnt/data", nil) 511 c.Assert(err, IsNil) 512 c.Assert(ubuntuDataDisk, Not(IsNil)) 513 c.Assert(ubuntuDataDisk.Dev(), Equals, "42:0") 514 515 // we have the ubuntu-seed, ubuntu-boot, and ubuntu-data partition labels 516 for _, label := range []string{"ubuntu-seed", "ubuntu-boot", "ubuntu-data"} { 517 id, err := ubuntuDataDisk.FindMatchingPartitionUUIDWithFsLabel(label) 518 c.Assert(err, IsNil) 519 c.Assert(id, Equals, label+"-partuuid") 520 } 521 522 // and the mountpoint for ubuntu-boot at /run/mnt/ubuntu-boot matches the 523 // same disk 524 matches, err := ubuntuDataDisk.MountPointIsFromDisk("/run/mnt/ubuntu-boot", nil) 525 c.Assert(err, IsNil) 526 c.Assert(matches, Equals, true) 527 528 // and we can find the partition for ubuntu-boot first and then match 529 // that with ubuntu-data too 530 ubuntuBootDisk, err := disks.DiskFromMountPoint("/run/mnt/ubuntu-boot", nil) 531 c.Assert(err, IsNil) 532 c.Assert(ubuntuBootDisk, Not(IsNil)) 533 c.Assert(ubuntuBootDisk.Dev(), Equals, "42:0") 534 535 // we have the ubuntu-seed, ubuntu-boot, and ubuntu-data partition labels 536 for _, label := range []string{"ubuntu-seed", "ubuntu-boot", "ubuntu-data"} { 537 id, err := ubuntuBootDisk.FindMatchingPartitionUUIDWithFsLabel(label) 538 c.Assert(err, IsNil) 539 c.Assert(id, Equals, label+"-partuuid") 540 } 541 542 // and the mountpoint for ubuntu-boot at /run/mnt/ubuntu-boot matches the 543 // same disk 544 matches, err = ubuntuBootDisk.MountPointIsFromDisk("/run/mnt/data", nil) 545 c.Assert(err, IsNil) 546 c.Assert(matches, Equals, true) 547 548 // finally we can't find the bios-boot partition because it has no fs label 549 _, err = ubuntuBootDisk.FindMatchingPartitionUUIDWithFsLabel("bios-boot") 550 c.Assert(err, ErrorMatches, "filesystem label \"bios-boot\" not found") 551 c.Assert(err, DeepEquals, disks.PartitionNotFoundError{ 552 SearchType: "filesystem-label", 553 SearchQuery: "bios-boot", 554 }) 555 556 _, err = ubuntuDataDisk.FindMatchingPartitionUUIDWithFsLabel("bios-boot") 557 c.Assert(err, ErrorMatches, "filesystem label \"bios-boot\" not found") 558 c.Assert(err, DeepEquals, disks.PartitionNotFoundError{ 559 SearchType: "filesystem-label", 560 SearchQuery: "bios-boot", 561 }) 562 563 // however we can find it via the partition label 564 uuid, err := ubuntuBootDisk.FindMatchingPartitionUUIDWithPartLabel("BIOS Boot") 565 c.Assert(err, IsNil) 566 c.Assert(uuid, Equals, "bios-boot-partuuid") 567 568 uuid, err = ubuntuDataDisk.FindMatchingPartitionUUIDWithPartLabel("BIOS Boot") 569 c.Assert(err, IsNil) 570 c.Assert(uuid, Equals, "bios-boot-partuuid") 571 572 // trying to find an unknown partition label fails however 573 _, err = ubuntuDataDisk.FindMatchingPartitionUUIDWithPartLabel("NOT BIOS Boot") 574 c.Assert(err, ErrorMatches, "partition label \"NOT BIOS Boot\" not found") 575 c.Assert(err, DeepEquals, disks.PartitionNotFoundError{ 576 SearchType: "partition-label", 577 SearchQuery: "NOT BIOS Boot", 578 }) 579 } 580 581 func (s *diskSuite) TestDiskFromMountPointDecryptedDevicePartitionsHappy(c *C) { 582 restore := osutil.MockMountInfo(`130 30 252:0 / /run/mnt/data rw,relatime shared:54 - ext4 /dev/mapper/ubuntu-data-3776bab4-8bcc-46b7-9da2-6a84ce7f93b4 rw 583 130 30 42:4 / /run/mnt/ubuntu-boot rw,relatime shared:54 - ext4 /dev/vda3 rw 584 `) 585 defer restore() 586 587 n := 0 588 restore = disks.MockUdevPropertiesForDevice(func(dev string) (map[string]string, error) { 589 n++ 590 switch n { 591 case 1: 592 // first request is to find the disk based on the mapper mount point 593 c.Assert(dev, Equals, "/dev/mapper/ubuntu-data-3776bab4-8bcc-46b7-9da2-6a84ce7f93b4") 594 // the mapper device is a disk/volume 595 return map[string]string{ 596 "DEVTYPE": "disk", 597 }, nil 598 case 2: 599 // next we find the physical disk by the dm uuid 600 c.Assert(dev, Equals, "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304") 601 return diskUdevPropMap, nil 602 case 3: 603 // then re-find the disk based on it's dev major / minor 604 c.Assert(dev, Equals, "/dev/block/42:0") 605 return diskUdevPropMap, nil 606 case 4: 607 // next find each partition in turn 608 c.Assert(dev, Equals, "vda1") 609 return biosBootUdevPropMap, nil 610 case 5: 611 c.Assert(dev, Equals, "vda2") 612 return ubuntuSeedUdevPropMap, nil 613 case 6: 614 c.Assert(dev, Equals, "vda3") 615 return ubuntuBootUdevPropMap, nil 616 case 7: 617 c.Assert(dev, Equals, "vda4") 618 return map[string]string{ 619 "ID_FS_LABEL_ENC": "ubuntu-data-enc", 620 "ID_PART_ENTRY_UUID": "ubuntu-data-enc-partuuid", 621 }, nil 622 case 8: 623 // next we will find the disk for a different mount point via 624 // MountPointIsFromDisk for ubuntu-boot 625 c.Assert(dev, Equals, "/dev/vda3") 626 return diskUdevPropMap, nil 627 case 9: 628 // next we will build up a disk from the ubuntu-boot mount point 629 c.Assert(dev, Equals, "/dev/vda3") 630 return diskUdevPropMap, nil 631 case 10: 632 // same as step 3 633 c.Assert(dev, Equals, "/dev/block/42:0") 634 return diskUdevPropMap, nil 635 case 11: 636 // next find each partition in turn again, same as steps 4-7 637 c.Assert(dev, Equals, "vda1") 638 return biosBootUdevPropMap, nil 639 case 12: 640 c.Assert(dev, Equals, "vda2") 641 return ubuntuSeedUdevPropMap, nil 642 case 13: 643 c.Assert(dev, Equals, "vda3") 644 return ubuntuBootUdevPropMap, nil 645 case 14: 646 c.Assert(dev, Equals, "vda4") 647 return map[string]string{ 648 "ID_FS_LABEL_ENC": "ubuntu-data-enc", 649 "ID_PART_ENTRY_UUID": "ubuntu-data-enc-partuuid", 650 }, nil 651 case 15: 652 // then we will find the disk for ubuntu-data mapper volume to 653 // verify it comes from the same disk as the second disk we just 654 // finished finding 655 c.Assert(dev, Equals, "/dev/mapper/ubuntu-data-3776bab4-8bcc-46b7-9da2-6a84ce7f93b4") 656 // the mapper device is a disk/volume 657 return map[string]string{ 658 "DEVTYPE": "disk", 659 }, nil 660 case 16: 661 // then we find the physical disk by the dm uuid 662 c.Assert(dev, Equals, "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304") 663 return diskUdevPropMap, nil 664 default: 665 c.Errorf("unexpected udev device properties requested (request %d): %s", n, dev) 666 return nil, fmt.Errorf("unexpected udev device (request %d): %s", n, dev) 667 } 668 }) 669 defer restore() 670 671 // mock the sysfs dm uuid and name files 672 dmDir := filepath.Join(filepath.Join(dirs.SysfsDir, "dev", "block"), "252:0", "dm") 673 err := os.MkdirAll(dmDir, 0755) 674 c.Assert(err, IsNil) 675 676 b := []byte("ubuntu-data-3776bab4-8bcc-46b7-9da2-6a84ce7f93b4") 677 err = ioutil.WriteFile(filepath.Join(dmDir, "name"), b, 0644) 678 c.Assert(err, IsNil) 679 680 b = []byte("CRYPT-LUKS2-5a522809c87e4dfa81a88dc5667d1304-ubuntu-data-3776bab4-8bcc-46b7-9da2-6a84ce7f93b4") 681 err = ioutil.WriteFile(filepath.Join(dmDir, "uuid"), b, 0644) 682 c.Assert(err, IsNil) 683 684 // mock the dev nodes in sysfs for the partitions 685 createVirtioDevicesInSysfs(c, map[string]bool{ 686 "vda1": true, 687 "vda2": true, 688 "vda3": true, 689 "vda4": true, 690 }) 691 692 opts := &disks.Options{IsDecryptedDevice: true} 693 ubuntuDataDisk, err := disks.DiskFromMountPoint("/run/mnt/data", opts) 694 c.Assert(err, IsNil) 695 c.Assert(ubuntuDataDisk, Not(IsNil)) 696 c.Assert(ubuntuDataDisk.Dev(), Equals, "42:0") 697 698 // we have the ubuntu-seed, ubuntu-boot, and ubuntu-data partition labels 699 for _, label := range []string{"ubuntu-seed", "ubuntu-boot", "ubuntu-data-enc"} { 700 id, err := ubuntuDataDisk.FindMatchingPartitionUUIDWithFsLabel(label) 701 c.Assert(err, IsNil) 702 c.Assert(id, Equals, label+"-partuuid") 703 } 704 705 // and the mountpoint for ubuntu-boot at /run/mnt/ubuntu-boot matches the 706 // same disk 707 matches, err := ubuntuDataDisk.MountPointIsFromDisk("/run/mnt/ubuntu-boot", nil) 708 c.Assert(err, IsNil) 709 c.Assert(matches, Equals, true) 710 711 // and we can find the partition for ubuntu-boot first and then match 712 // that with ubuntu-data too 713 ubuntuBootDisk, err := disks.DiskFromMountPoint("/run/mnt/ubuntu-boot", nil) 714 c.Assert(err, IsNil) 715 c.Assert(ubuntuBootDisk, Not(IsNil)) 716 c.Assert(ubuntuBootDisk.Dev(), Equals, "42:0") 717 718 // we have the ubuntu-seed, ubuntu-boot, and ubuntu-data partition labels 719 for _, label := range []string{"ubuntu-seed", "ubuntu-boot", "ubuntu-data-enc"} { 720 id, err := ubuntuBootDisk.FindMatchingPartitionUUIDWithFsLabel(label) 721 c.Assert(err, IsNil) 722 c.Assert(id, Equals, label+"-partuuid") 723 } 724 725 // and the mountpoint for ubuntu-boot at /run/mnt/ubuntu-boot matches the 726 // same disk 727 matches, err = ubuntuBootDisk.MountPointIsFromDisk("/run/mnt/data", opts) 728 c.Assert(err, IsNil) 729 c.Assert(matches, Equals, true) 730 }