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