github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/gadget/device_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 "errors" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 "strings" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/gadget" 34 "github.com/snapcore/snapd/osutil" 35 ) 36 37 type deviceSuite struct { 38 dir string 39 } 40 41 var _ = Suite(&deviceSuite{}) 42 43 func (d *deviceSuite) SetUpTest(c *C) { 44 d.dir = c.MkDir() 45 dirs.SetRootDir(d.dir) 46 47 err := os.MkdirAll(filepath.Join(d.dir, "/dev/disk/by-label"), 0755) 48 c.Assert(err, IsNil) 49 err = os.MkdirAll(filepath.Join(d.dir, "/dev/disk/by-partlabel"), 0755) 50 c.Assert(err, IsNil) 51 err = os.MkdirAll(filepath.Join(d.dir, "/dev/mapper"), 0755) 52 c.Assert(err, IsNil) 53 err = ioutil.WriteFile(filepath.Join(d.dir, "/dev/fakedevice"), []byte(""), 0644) 54 c.Assert(err, IsNil) 55 } 56 57 func (d *deviceSuite) TearDownTest(c *C) { 58 dirs.SetRootDir("/") 59 } 60 61 func (d *deviceSuite) setupMockSysfs(c *C) { 62 // setup everything for 'writable' 63 err := ioutil.WriteFile(filepath.Join(d.dir, "/dev/fakedevice0p1"), []byte(""), 0644) 64 c.Assert(err, IsNil) 65 err = os.Symlink("../../fakedevice0p1", filepath.Join(d.dir, "/dev/disk/by-label/writable")) 66 c.Assert(err, IsNil) 67 // make parent device 68 err = ioutil.WriteFile(filepath.Join(d.dir, "/dev/fakedevice0"), []byte(""), 0644) 69 c.Assert(err, IsNil) 70 // and fake /sys/block structure 71 err = os.MkdirAll(filepath.Join(d.dir, "/sys/block/fakedevice0/fakedevice0p1"), 0755) 72 c.Assert(err, IsNil) 73 } 74 75 func (d *deviceSuite) setupMockSysfsForDevMapper(c *C) { 76 // setup a mock /dev/mapper environment (incomplete we have no "happy" 77 // test; use a complex setup that mimics LVM in LUKS: 78 // /dev/mapper/data_crypt (symlink) 79 // ⤷ /dev/dm-1 (LVM) 80 // ⤷ /dev/dm-0 (LUKS) 81 // ⤷ /dev/fakedevice0 (actual device) 82 err := ioutil.WriteFile(filepath.Join(d.dir, "/dev/dm-0"), nil, 0644) 83 c.Assert(err, IsNil) 84 err = ioutil.WriteFile(filepath.Join(d.dir, "/dev/dm-1"), nil, 0644) 85 c.Assert(err, IsNil) 86 err = ioutil.WriteFile(filepath.Join(d.dir, "/dev/fakedevice0"), []byte(""), 0644) 87 c.Assert(err, IsNil) 88 err = ioutil.WriteFile(filepath.Join(d.dir, "/dev/fakedevice"), []byte(""), 0644) 89 c.Assert(err, IsNil) 90 // symlinks added by dm/udev are relative 91 err = os.Symlink("../dm-1", filepath.Join(d.dir, "/dev/mapper/data_crypt")) 92 c.Assert(err, IsNil) 93 err = os.MkdirAll(filepath.Join(d.dir, "/sys/block/dm-1/slaves/"), 0755) 94 c.Assert(err, IsNil) 95 // sys symlinks are relative too 96 err = os.Symlink("../../dm-0", filepath.Join(d.dir, "/sys/block/dm-1/slaves/dm-0")) 97 c.Assert(err, IsNil) 98 err = os.MkdirAll(filepath.Join(d.dir, "/sys/block/dm-0/slaves/"), 0755) 99 c.Assert(err, IsNil) 100 // real symlink would point to ../../../../<bus, eg. pci>/<addr>/block/fakedevice/fakedevice0 101 err = os.Symlink("../../../../fakedevice/fakedevice0", filepath.Join(d.dir, "/sys/block/dm-0/slaves/fakedevice0")) 102 c.Assert(err, IsNil) 103 err = os.MkdirAll(filepath.Join(d.dir, "/sys/block/fakedevice/fakedevice0"), 0755) 104 c.Assert(err, IsNil) 105 } 106 107 func (d *deviceSuite) TestDeviceFindByStructureName(c *C) { 108 names := []struct { 109 escaped string 110 structure string 111 }{ 112 {"foo", "foo"}, 113 {"123", "123"}, 114 {"foo\\x20bar", "foo bar"}, 115 {"foo#bar", "foo#bar"}, 116 {"Новый_том", "Новый_том"}, 117 {`pinkié\x20pie`, `pinkié pie`}, 118 } 119 for _, name := range names { 120 err := os.Symlink(filepath.Join(d.dir, "/dev/fakedevice"), filepath.Join(d.dir, "/dev/disk/by-partlabel", name.escaped)) 121 c.Assert(err, IsNil) 122 } 123 124 for _, tc := range names { 125 c.Logf("trying: %q", tc) 126 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 127 VolumeStructure: &gadget.VolumeStructure{Name: tc.structure}, 128 }) 129 c.Check(err, IsNil) 130 c.Check(found, Equals, filepath.Join(d.dir, "/dev/fakedevice")) 131 } 132 } 133 134 func (d *deviceSuite) TestDeviceFindRelativeSymlink(c *C) { 135 err := os.Symlink("../../fakedevice", filepath.Join(d.dir, "/dev/disk/by-partlabel/relative")) 136 c.Assert(err, IsNil) 137 138 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 139 VolumeStructure: &gadget.VolumeStructure{Name: "relative"}, 140 }) 141 c.Check(err, IsNil) 142 c.Check(found, Equals, filepath.Join(d.dir, "/dev/fakedevice")) 143 } 144 145 func (d *deviceSuite) TestDeviceFindByFilesystemLabel(c *C) { 146 names := []struct { 147 escaped string 148 structure string 149 }{ 150 {"foo", "foo"}, 151 {"123", "123"}, 152 {`foo\x20bar`, "foo bar"}, 153 {"foo#bar", "foo#bar"}, 154 {"Новый_том", "Новый_том"}, 155 {`pinkié\x20pie`, `pinkié pie`}, 156 } 157 for _, name := range names { 158 err := os.Symlink(filepath.Join(d.dir, "/dev/fakedevice"), filepath.Join(d.dir, "/dev/disk/by-label", name.escaped)) 159 c.Assert(err, IsNil) 160 } 161 162 for _, tc := range names { 163 c.Logf("trying: %q", tc) 164 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 165 VolumeStructure: &gadget.VolumeStructure{ 166 Filesystem: "ext4", 167 Label: tc.structure, 168 }, 169 }) 170 c.Check(err, IsNil) 171 c.Check(found, Equals, filepath.Join(d.dir, "/dev/fakedevice")) 172 } 173 } 174 175 func (d *deviceSuite) TestDeviceFindChecksPartlabelAndFilesystemLabelHappy(c *C) { 176 fakedevice := filepath.Join(d.dir, "/dev/fakedevice") 177 err := os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/foo")) 178 c.Assert(err, IsNil) 179 180 err = os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-partlabel/bar")) 181 c.Assert(err, IsNil) 182 183 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 184 VolumeStructure: &gadget.VolumeStructure{ 185 Name: "bar", 186 Label: "foo", 187 }, 188 }) 189 c.Check(err, IsNil) 190 c.Check(found, Equals, filepath.Join(d.dir, "/dev/fakedevice")) 191 } 192 193 func (d *deviceSuite) TestDeviceFindFilesystemLabelToNameFallback(c *C) { 194 fakedevice := filepath.Join(d.dir, "/dev/fakedevice") 195 // only the by-filesystem-label symlink 196 err := os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/foo")) 197 c.Assert(err, IsNil) 198 199 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 200 VolumeStructure: &gadget.VolumeStructure{ 201 Name: "foo", 202 Filesystem: "ext4", 203 }, 204 }) 205 c.Check(err, IsNil) 206 c.Check(found, Equals, filepath.Join(d.dir, "/dev/fakedevice")) 207 } 208 209 func (d *deviceSuite) TestDeviceFindChecksPartlabelAndFilesystemLabelMismatch(c *C) { 210 fakedevice := filepath.Join(d.dir, "/dev/fakedevice") 211 err := os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/foo")) 212 c.Assert(err, IsNil) 213 214 // partlabel of the structure points to a different device 215 fakedeviceOther := filepath.Join(d.dir, "/dev/fakedevice-other") 216 err = ioutil.WriteFile(fakedeviceOther, []byte(""), 0644) 217 c.Assert(err, IsNil) 218 err = os.Symlink(fakedeviceOther, filepath.Join(d.dir, "/dev/disk/by-partlabel/bar")) 219 c.Assert(err, IsNil) 220 221 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 222 VolumeStructure: &gadget.VolumeStructure{ 223 Name: "bar", 224 Label: "foo", 225 Filesystem: "ext4", 226 }, 227 }) 228 c.Check(err, ErrorMatches, `conflicting device match, ".*/by-label/foo" points to ".*/fakedevice", previous match ".*/by-partlabel/bar" points to ".*/fakedevice-other"`) 229 c.Check(found, Equals, "") 230 } 231 232 func (d *deviceSuite) TestDeviceFindNotFound(c *C) { 233 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 234 VolumeStructure: &gadget.VolumeStructure{ 235 Name: "bar", 236 Label: "foo", 237 }, 238 }) 239 c.Check(err, ErrorMatches, `device not found`) 240 c.Check(found, Equals, "") 241 } 242 243 func (d *deviceSuite) TestDeviceFindNotFoundEmpty(c *C) { 244 // neither name nor filesystem label set 245 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 246 VolumeStructure: &gadget.VolumeStructure{ 247 Name: "", 248 // structure has no filesystem, fs label check is 249 // ineffective 250 Label: "", 251 }, 252 }) 253 c.Check(err, ErrorMatches, `device not found`) 254 c.Check(found, Equals, "") 255 256 // try with proper filesystem now 257 found, err = gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 258 VolumeStructure: &gadget.VolumeStructure{ 259 Name: "", 260 Label: "", 261 Filesystem: "ext4", 262 }, 263 }) 264 c.Check(err, ErrorMatches, `device not found`) 265 c.Check(found, Equals, "") 266 } 267 268 func (d *deviceSuite) TestDeviceFindNotFoundSymlinkPointsNowhere(c *C) { 269 fakedevice := filepath.Join(d.dir, "/dev/fakedevice-not-found") 270 err := os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/foo")) 271 c.Assert(err, IsNil) 272 273 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 274 VolumeStructure: &gadget.VolumeStructure{ 275 Label: "foo", 276 }, 277 }) 278 c.Check(err, ErrorMatches, `device not found`) 279 c.Check(found, Equals, "") 280 } 281 282 func (d *deviceSuite) TestDeviceFindNotFoundNotASymlink(c *C) { 283 err := ioutil.WriteFile(filepath.Join(d.dir, "/dev/disk/by-label/foo"), nil, 0644) 284 c.Assert(err, IsNil) 285 286 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 287 VolumeStructure: &gadget.VolumeStructure{ 288 Filesystem: "ext4", 289 Label: "foo", 290 }, 291 }) 292 c.Check(err, ErrorMatches, `candidate .*/dev/disk/by-label/foo is not a symlink`) 293 c.Check(found, Equals, "") 294 } 295 296 func (d *deviceSuite) TestDeviceFindBadEvalSymlinks(c *C) { 297 fakedevice := filepath.Join(d.dir, "/dev/fakedevice") 298 fooSymlink := filepath.Join(d.dir, "/dev/disk/by-label/foo") 299 err := os.Symlink(fakedevice, fooSymlink) 300 c.Assert(err, IsNil) 301 302 restore := gadget.MockEvalSymlinks(func(p string) (string, error) { 303 c.Assert(p, Equals, fooSymlink) 304 return "", errors.New("failed") 305 }) 306 defer restore() 307 308 found, err := gadget.FindDeviceForStructure(&gadget.LaidOutStructure{ 309 VolumeStructure: &gadget.VolumeStructure{ 310 Filesystem: "vfat", 311 Label: "foo", 312 }, 313 }) 314 c.Check(err, ErrorMatches, `cannot read device link: failed`) 315 c.Check(found, Equals, "") 316 } 317 318 var writableMountInfoFmt = `26 27 8:3 / /writable rw,relatime shared:7 - ext4 %s/dev/fakedevice0p1 rw,data=ordered` 319 320 func (d *deviceSuite) TestDeviceFindFallbackNotFoundNoWritable(c *C) { 321 badMountInfoFmt := `26 27 8:3 / /not-writable rw,relatime shared:7 - ext4 %s/dev/fakedevice0p1 rw,data=ordered` 322 restore := osutil.MockMountInfo(fmt.Sprintf(badMountInfoFmt, d.dir)) 323 defer restore() 324 325 found, offs, err := gadget.FindDeviceForStructureWithFallback(&gadget.LaidOutStructure{ 326 VolumeStructure: &gadget.VolumeStructure{ 327 Type: "bare", 328 }, 329 StartOffset: 123, 330 }) 331 c.Check(err, ErrorMatches, `device not found`) 332 c.Check(found, Equals, "") 333 c.Check(offs, Equals, gadget.Size(0)) 334 } 335 336 func (d *deviceSuite) TestDeviceFindFallbackBadWritable(c *C) { 337 restore := osutil.MockMountInfo(fmt.Sprintf(writableMountInfoFmt, d.dir)) 338 defer restore() 339 340 ps := &gadget.LaidOutStructure{ 341 VolumeStructure: &gadget.VolumeStructure{ 342 Type: "bare", 343 }, 344 StartOffset: 123, 345 } 346 347 found, offs, err := gadget.FindDeviceForStructureWithFallback(ps) 348 c.Check(err, ErrorMatches, `lstat .*/dev/fakedevice0p1: no such file or directory`) 349 c.Check(found, Equals, "") 350 c.Check(offs, Equals, gadget.Size(0)) 351 352 c.Assert(ioutil.WriteFile(filepath.Join(d.dir, "dev/fakedevice0p1"), nil, 064), IsNil) 353 354 found, offs, err = gadget.FindDeviceForStructureWithFallback(ps) 355 c.Check(err, ErrorMatches, `unexpected number of matches \(0\) for /sys/block/\*/fakedevice0p1`) 356 c.Check(found, Equals, "") 357 c.Check(offs, Equals, gadget.Size(0)) 358 359 err = os.MkdirAll(filepath.Join(d.dir, "/sys/block/fakedevice0/fakedevice0p1"), 0755) 360 c.Assert(err, IsNil) 361 362 found, offs, err = gadget.FindDeviceForStructureWithFallback(ps) 363 c.Check(err, ErrorMatches, `device .*/dev/fakedevice0 does not exist`) 364 c.Check(found, Equals, "") 365 c.Check(offs, Equals, gadget.Size(0)) 366 } 367 368 func (d *deviceSuite) TestDeviceFindFallbackHappyWritable(c *C) { 369 d.setupMockSysfs(c) 370 restore := osutil.MockMountInfo(fmt.Sprintf(writableMountInfoFmt, d.dir)) 371 defer restore() 372 373 psJustBare := &gadget.LaidOutStructure{ 374 VolumeStructure: &gadget.VolumeStructure{ 375 Type: "bare", 376 }, 377 StartOffset: 123, 378 } 379 psBareWithName := &gadget.LaidOutStructure{ 380 VolumeStructure: &gadget.VolumeStructure{ 381 Type: "bare", 382 Name: "foo", 383 }, 384 StartOffset: 123, 385 } 386 psMBR := &gadget.LaidOutStructure{ 387 VolumeStructure: &gadget.VolumeStructure{ 388 Type: "mbr", 389 Name: "mbr", 390 }, 391 StartOffset: 0, 392 } 393 psNoName := &gadget.LaidOutStructure{ 394 VolumeStructure: &gadget.VolumeStructure{}, 395 StartOffset: 123, 396 } 397 398 for _, ps := range []*gadget.LaidOutStructure{psJustBare, psBareWithName, psNoName, psMBR} { 399 found, offs, err := gadget.FindDeviceForStructureWithFallback(ps) 400 c.Check(err, IsNil) 401 c.Check(found, Equals, filepath.Join(d.dir, "/dev/fakedevice0")) 402 if ps.Type != "mbr" { 403 c.Check(offs, Equals, gadget.Size(123)) 404 } else { 405 c.Check(offs, Equals, gadget.Size(0)) 406 } 407 } 408 } 409 410 func (d *deviceSuite) TestDeviceFindFallbackNotForNamedWritable(c *C) { 411 d.setupMockSysfs(c) 412 restore := osutil.MockMountInfo(fmt.Sprintf(writableMountInfoFmt, d.dir)) 413 defer restore() 414 415 // should not hit the fallback path 416 psNamed := &gadget.LaidOutStructure{ 417 VolumeStructure: &gadget.VolumeStructure{ 418 Name: "foo", 419 }, 420 StartOffset: 123, 421 } 422 found, offs, err := gadget.FindDeviceForStructureWithFallback(psNamed) 423 c.Check(err, Equals, gadget.ErrDeviceNotFound) 424 c.Check(found, Equals, "") 425 c.Check(offs, Equals, gadget.Size(0)) 426 } 427 428 func (d *deviceSuite) TestDeviceFindFallbackNotForFilesystem(c *C) { 429 d.setupMockSysfs(c) 430 restore := osutil.MockMountInfo(writableMountInfoFmt) 431 defer restore() 432 433 psFs := &gadget.LaidOutStructure{ 434 VolumeStructure: &gadget.VolumeStructure{ 435 Label: "foo", 436 Filesystem: "ext4", 437 }, 438 StartOffset: 123, 439 } 440 found, offs, err := gadget.FindDeviceForStructureWithFallback(psFs) 441 c.Check(err, ErrorMatches, "internal error: cannot use with filesystem structures") 442 c.Check(found, Equals, "") 443 c.Check(offs, Equals, gadget.Size(0)) 444 } 445 446 func (d *deviceSuite) TestDeviceFindFallbackBadMountInfo(c *C) { 447 d.setupMockSysfs(c) 448 restore := osutil.MockMountInfo("garbage") 449 defer restore() 450 psFs := &gadget.LaidOutStructure{ 451 VolumeStructure: &gadget.VolumeStructure{ 452 Name: "foo", 453 Type: "bare", 454 }, 455 StartOffset: 123, 456 } 457 found, offs, err := gadget.FindDeviceForStructureWithFallback(psFs) 458 c.Check(err, ErrorMatches, "cannot read mount info: .*") 459 c.Check(found, Equals, "") 460 c.Check(offs, Equals, gadget.Size(0)) 461 } 462 463 func (d *deviceSuite) TestDeviceFindFallbackPassThrough(c *C) { 464 err := ioutil.WriteFile(filepath.Join(d.dir, "/dev/disk/by-partlabel/foo"), nil, 0644) 465 c.Assert(err, IsNil) 466 467 ps := &gadget.LaidOutStructure{ 468 VolumeStructure: &gadget.VolumeStructure{ 469 Name: "foo", 470 }, 471 } 472 found, offs, err := gadget.FindDeviceForStructureWithFallback(ps) 473 c.Check(err, ErrorMatches, `candidate .*/dev/disk/by-partlabel/foo is not a symlink`) 474 c.Check(found, Equals, "") 475 c.Check(offs, Equals, gadget.Size(0)) 476 477 // create a proper symlink 478 err = os.Remove(filepath.Join(d.dir, "/dev/disk/by-partlabel/foo")) 479 c.Assert(err, IsNil) 480 err = os.Symlink("../../fakedevice", filepath.Join(d.dir, "/dev/disk/by-partlabel/foo")) 481 c.Assert(err, IsNil) 482 483 // this should be happy again 484 found, offs, err = gadget.FindDeviceForStructureWithFallback(ps) 485 c.Assert(err, IsNil) 486 c.Check(found, Equals, filepath.Join(d.dir, "/dev/fakedevice")) 487 c.Check(offs, Equals, gadget.Size(0)) 488 } 489 490 func (d *deviceSuite) TestDeviceFindMountPointErrorsWithBare(c *C) { 491 p, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 492 VolumeStructure: &gadget.VolumeStructure{ 493 // no filesystem 494 Filesystem: "", 495 }, 496 }) 497 c.Assert(err, ErrorMatches, "no filesystem defined") 498 c.Check(p, Equals, "") 499 500 p, err = gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 501 VolumeStructure: &gadget.VolumeStructure{ 502 // also counts as bare structure 503 Filesystem: "none", 504 }, 505 }) 506 c.Assert(err, ErrorMatches, "no filesystem defined") 507 c.Check(p, Equals, "") 508 } 509 510 func (d *deviceSuite) TestDeviceFindMountPointErrorsFromDevice(c *C) { 511 p, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 512 VolumeStructure: &gadget.VolumeStructure{ 513 Label: "bar", 514 Filesystem: "ext4", 515 }, 516 }) 517 c.Assert(err, ErrorMatches, "device not found") 518 c.Check(p, Equals, "") 519 520 p, err = gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 521 VolumeStructure: &gadget.VolumeStructure{ 522 Name: "bar", 523 Filesystem: "ext4", 524 }, 525 }) 526 c.Assert(err, ErrorMatches, "device not found") 527 c.Check(p, Equals, "") 528 } 529 530 func (d *deviceSuite) TestDeviceFindMountPointErrorBadMountinfo(c *C) { 531 // taken from core18 system 532 533 fakedevice := filepath.Join(d.dir, "/dev/sda2") 534 err := ioutil.WriteFile(fakedevice, []byte(""), 0644) 535 c.Assert(err, IsNil) 536 err = os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/system-boot")) 537 c.Assert(err, IsNil) 538 restore := osutil.MockMountInfo("garbage") 539 defer restore() 540 541 found, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 542 VolumeStructure: &gadget.VolumeStructure{ 543 Name: "EFI System", 544 Label: "system-boot", 545 Filesystem: "vfat", 546 }, 547 }) 548 c.Check(err, ErrorMatches, "cannot read mount info: .*") 549 c.Check(found, Equals, "") 550 } 551 552 func (d *deviceSuite) TestDeviceFindMountPointByLabeHappySimple(c *C) { 553 // taken from core18 system 554 555 fakedevice := filepath.Join(d.dir, "/dev/sda2") 556 err := ioutil.WriteFile(fakedevice, []byte(""), 0644) 557 c.Assert(err, IsNil) 558 err = os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/system-boot")) 559 c.Assert(err, IsNil) 560 err = os.Symlink(fakedevice, filepath.Join(d.dir, `/dev/disk/by-partlabel/EFI\x20System`)) 561 c.Assert(err, IsNil) 562 563 mountInfo := ` 564 170 27 8:2 / /boot/efi rw,relatime shared:58 - vfat ${rootDir}/dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 565 172 27 8:2 /EFI/ubuntu /boot/grub rw,relatime shared:58 - vfat ${rootDir}/dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 566 ` 567 restore := osutil.MockMountInfo(strings.Replace(mountInfo[1:], "${rootDir}", d.dir, -1)) 568 defer restore() 569 570 found, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 571 VolumeStructure: &gadget.VolumeStructure{ 572 Name: "EFI System", 573 Label: "system-boot", 574 Filesystem: "vfat", 575 }, 576 }) 577 c.Check(err, IsNil) 578 c.Check(found, Equals, "/boot/efi") 579 } 580 581 func (d *deviceSuite) TestDeviceFindMountPointByLabeHappyReversed(c *C) { 582 // taken from core18 system 583 584 fakedevice := filepath.Join(d.dir, "/dev/sda2") 585 err := ioutil.WriteFile(fakedevice, []byte(""), 0644) 586 c.Assert(err, IsNil) 587 // single property match 588 err = os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/system-boot")) 589 c.Assert(err, IsNil) 590 591 // reverse the order of lines 592 mountInfoReversed := ` 593 172 27 8:2 /EFI/ubuntu /boot/grub rw,relatime shared:58 - vfat ${rootDir}/dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 594 170 27 8:2 / /boot/efi rw,relatime shared:58 - vfat ${rootDir}/dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 595 ` 596 597 restore := osutil.MockMountInfo(strings.Replace(mountInfoReversed[1:], "${rootDir}", d.dir, -1)) 598 defer restore() 599 600 found, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 601 VolumeStructure: &gadget.VolumeStructure{ 602 Name: "EFI System", 603 Label: "system-boot", 604 Filesystem: "vfat", 605 }, 606 }) 607 c.Check(err, IsNil) 608 c.Check(found, Equals, "/boot/efi") 609 } 610 611 func (d *deviceSuite) TestDeviceFindMountPointPicksFirstMatch(c *C) { 612 // taken from core18 system 613 614 fakedevice := filepath.Join(d.dir, "/dev/sda2") 615 err := ioutil.WriteFile(fakedevice, []byte(""), 0644) 616 c.Assert(err, IsNil) 617 // single property match 618 err = os.Symlink(fakedevice, filepath.Join(d.dir, "/dev/disk/by-label/system-boot")) 619 c.Assert(err, IsNil) 620 621 mountInfo := ` 622 852 134 8:2 / /mnt/foo rw,relatime shared:58 - vfat ${rootDir}/dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 623 172 27 8:2 /EFI/ubuntu /boot/grub rw,relatime shared:58 - vfat ${rootDir}/dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 624 170 27 8:2 / /boot/efi rw,relatime shared:58 - vfat ${rootDir}/dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro 625 ` 626 627 restore := osutil.MockMountInfo(strings.Replace(mountInfo[1:], "${rootDir}", d.dir, -1)) 628 defer restore() 629 630 found, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 631 VolumeStructure: &gadget.VolumeStructure{ 632 Name: "EFI System", 633 Label: "system-boot", 634 Filesystem: "vfat", 635 }, 636 }) 637 c.Check(err, IsNil) 638 c.Check(found, Equals, "/mnt/foo") 639 } 640 641 func (d *deviceSuite) TestDeviceFindMountPointByPartlabel(c *C) { 642 fakedevice := filepath.Join(d.dir, "/dev/fakedevice") 643 err := ioutil.WriteFile(fakedevice, []byte(""), 0644) 644 c.Assert(err, IsNil) 645 err = os.Symlink(fakedevice, filepath.Join(d.dir, `/dev/disk/by-partlabel/pinkié\x20pie`)) 646 c.Assert(err, IsNil) 647 648 mountInfo := ` 649 170 27 8:2 / /mount-point rw,relatime shared:58 - ext4 ${rootDir}/dev/fakedevice rw 650 ` 651 652 restore := osutil.MockMountInfo(strings.Replace(mountInfo[1:], "${rootDir}", d.dir, -1)) 653 defer restore() 654 655 found, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 656 VolumeStructure: &gadget.VolumeStructure{ 657 Name: "pinkié pie", 658 Filesystem: "ext4", 659 }, 660 }) 661 c.Check(err, IsNil) 662 c.Check(found, Equals, "/mount-point") 663 } 664 665 func (d *deviceSuite) TestDeviceFindMountPointChecksFilesystem(c *C) { 666 fakedevice := filepath.Join(d.dir, "/dev/fakedevice") 667 err := ioutil.WriteFile(fakedevice, []byte(""), 0644) 668 c.Assert(err, IsNil) 669 err = os.Symlink(fakedevice, filepath.Join(d.dir, `/dev/disk/by-partlabel/label`)) 670 c.Assert(err, IsNil) 671 672 mountInfo := ` 673 170 27 8:2 / /mount-point rw,relatime shared:58 - vfat ${rootDir}/dev/fakedevice rw 674 ` 675 676 restore := osutil.MockMountInfo(strings.Replace(mountInfo[1:], "${rootDir}", d.dir, -1)) 677 defer restore() 678 679 found, err := gadget.FindMountPointForStructure(&gadget.LaidOutStructure{ 680 VolumeStructure: &gadget.VolumeStructure{ 681 Name: "label", 682 // different fs than mount entry 683 Filesystem: "ext4", 684 }, 685 }) 686 c.Check(err, ErrorMatches, "mount point not found") 687 c.Check(found, Equals, "") 688 } 689 690 func (d *deviceSuite) TestParentDiskFromMountSource(c *C) { 691 d.setupMockSysfs(c) 692 693 disk, err := gadget.ParentDiskFromMountSource(filepath.Join(dirs.GlobalRootDir, "/dev/fakedevice0p1")) 694 c.Assert(err, IsNil) 695 c.Check(disk, Matches, ".*/dev/fakedevice0") 696 } 697 698 func (d *deviceSuite) TestParentDiskFromMountSourceBadSymlinkErr(c *C) { 699 d.setupMockSysfs(c) 700 701 err := os.Symlink("../bad-target", filepath.Join(d.dir, "/dev/mapper/bad-target-symlink")) 702 c.Assert(err, IsNil) 703 704 _, err = gadget.ParentDiskFromMountSource(filepath.Join(dirs.GlobalRootDir, "/dev/mapper/bad-target-symlink")) 705 c.Assert(err, ErrorMatches, `cannot resolve mount source symlink .*/dev/mapper/bad-target-symlink: lstat .*/dev/bad-target: no such file or directory`) 706 } 707 708 func (d *deviceSuite) TestParentDiskFromMountSourceDeviceMapperHappy(c *C) { 709 d.setupMockSysfsForDevMapper(c) 710 711 disk, err := gadget.ParentDiskFromMountSource(filepath.Join(dirs.GlobalRootDir, "/dev/mapper/data_crypt")) 712 713 c.Assert(err, IsNil) 714 c.Check(disk, Matches, ".*/dev/fakedevice") 715 } 716 717 func (d *deviceSuite) TestParentDiskFromMountSourceDeviceMapperErrGlob(c *C) { 718 d.setupMockSysfsForDevMapper(c) 719 720 // break the intermediate slaves directory 721 c.Assert(os.RemoveAll(filepath.Join(d.dir, "/sys/block/dm-0/slaves/fakedevice0")), IsNil) 722 723 _, err := gadget.ParentDiskFromMountSource(filepath.Join(dirs.GlobalRootDir, "/dev/mapper/data_crypt")) 724 c.Assert(err, ErrorMatches, "cannot resolve device mapper device dm-1: unexpected number of dm device dm-0 slaves: 0") 725 726 c.Assert(os.Chmod(filepath.Join(d.dir, "/sys/block/dm-0"), 0000), IsNil) 727 defer os.Chmod(filepath.Join(d.dir, "/sys/block/dm-0"), 0755) 728 729 _, err = gadget.ParentDiskFromMountSource(filepath.Join(dirs.GlobalRootDir, "/dev/mapper/data_crypt")) 730 c.Assert(err, ErrorMatches, "cannot resolve device mapper device dm-1: unexpected number of dm device dm-0 slaves: 0") 731 } 732 733 func (d *deviceSuite) TestParentDiskFromMountSourceDeviceMapperErrTargetDevice(c *C) { 734 d.setupMockSysfsForDevMapper(c) 735 736 c.Assert(os.RemoveAll(filepath.Join(d.dir, "/sys/block/fakedevice")), IsNil) 737 738 _, err := gadget.ParentDiskFromMountSource(filepath.Join(dirs.GlobalRootDir, "/dev/mapper/data_crypt")) 739 c.Assert(err, ErrorMatches, `unexpected number of matches \(0\) for /sys/block/\*/fakedevice0`) 740 } 741 742 func (d *deviceSuite) TestParentDiskFromMountSourceDeviceMapperLevels(c *C) { 743 err := os.Symlink("../dm-6", filepath.Join(d.dir, "/dev/mapper/data_crypt")) 744 c.Assert(err, IsNil) 745 for i := 6; i > 0; i-- { 746 err := ioutil.WriteFile(filepath.Join(d.dir, fmt.Sprintf("/dev/dm-%v", i)), nil, 0644) 747 c.Assert(err, IsNil) 748 err = os.MkdirAll(filepath.Join(d.dir, fmt.Sprintf("/sys/block/dm-%v/slaves/", i)), 0755) 749 c.Assert(err, IsNil) 750 // sys symlinks are relative too 751 err = os.Symlink(fmt.Sprintf("../../dm-%v", i-1), filepath.Join(d.dir, fmt.Sprintf("/sys/block/dm-%v/slaves/dm-%v", i, i-1))) 752 c.Assert(err, IsNil) 753 } 754 755 _, err = gadget.ParentDiskFromMountSource(filepath.Join(dirs.GlobalRootDir, "/dev/mapper/data_crypt")) 756 c.Assert(err, ErrorMatches, `cannot resolve device mapper device dm-6: too many levels`) 757 }