github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/disks/mockdisk_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  
    25  	. "gopkg.in/check.v1"
    26  
    27  	"github.com/snapcore/snapd/dirs"
    28  	"github.com/snapcore/snapd/osutil/disks"
    29  	"github.com/snapcore/snapd/testutil"
    30  )
    31  
    32  type mockDiskSuite struct {
    33  	testutil.BaseTest
    34  }
    35  
    36  var _ = Suite(&mockDiskSuite{})
    37  
    38  func (s *mockDiskSuite) SetUpTest(c *C) {
    39  	dirs.SetRootDir(c.MkDir())
    40  }
    41  
    42  func (s *mockDiskSuite) TestMockDeviceNameDisksToPartitionMapping(c *C) {
    43  	// one disk with different device names
    44  	d1 := &disks.MockDiskMapping{
    45  		FilesystemLabelToPartUUID: map[string]string{
    46  			"label1": "part1",
    47  		},
    48  		DiskHasPartitions: true,
    49  		DevNum:            "d1",
    50  	}
    51  
    52  	d2 := &disks.MockDiskMapping{
    53  		FilesystemLabelToPartUUID: map[string]string{
    54  			"label2": "part2",
    55  		},
    56  		DiskHasPartitions: true,
    57  		DevNum:            "d2",
    58  	}
    59  
    60  	m := map[string]*disks.MockDiskMapping{
    61  		"devName1":   d1,
    62  		"devName2":   d1,
    63  		"other-disk": d2,
    64  	}
    65  
    66  	r := disks.MockDeviceNameDisksToPartitionMapping(m)
    67  	defer r()
    68  
    69  	res, err := disks.DiskFromDeviceName("devName1")
    70  	c.Assert(err, IsNil)
    71  	c.Assert(res, DeepEquals, d1)
    72  
    73  	res2, err := disks.DiskFromDeviceName("devName2")
    74  	c.Assert(err, IsNil)
    75  	c.Assert(res2, DeepEquals, d1)
    76  
    77  	_, err = disks.DiskFromDeviceName("devName3")
    78  	c.Assert(err, ErrorMatches, fmt.Sprintf("device name %q not mocked", "devName3"))
    79  
    80  	res3, err := disks.DiskFromDeviceName("other-disk")
    81  	c.Assert(err, IsNil)
    82  	c.Assert(res3, DeepEquals, d2)
    83  }
    84  
    85  func (s *mockDiskSuite) TestMockMountPointDisksToPartitionMappingVerifiesUniqueness(c *C) {
    86  	// two different disks with different DevNum's
    87  	d1 := &disks.MockDiskMapping{
    88  		FilesystemLabelToPartUUID: map[string]string{
    89  			"label1": "part1",
    90  		},
    91  		DiskHasPartitions: true,
    92  		DevNum:            "d1",
    93  	}
    94  
    95  	d2 := &disks.MockDiskMapping{
    96  		FilesystemLabelToPartUUID: map[string]string{
    97  			"label1": "part1",
    98  		},
    99  		DiskHasPartitions: false,
   100  		DevNum:            "d2",
   101  	}
   102  
   103  	// the pointers are different, and they are not the same
   104  	c.Assert(d1, Not(Equals), d2)
   105  	c.Assert(d1, Not(DeepEquals), d2)
   106  
   107  	m := map[disks.Mountpoint]*disks.MockDiskMapping{
   108  		{Mountpoint: "mount1"}: d1,
   109  		{Mountpoint: "mount2"}: d1,
   110  		{Mountpoint: "mount3"}: d2,
   111  	}
   112  
   113  	// mocking works
   114  	r := disks.MockMountPointDisksToPartitionMapping(m)
   115  	defer r()
   116  
   117  	// changing so they have the same DevNum doesn't work though
   118  	d2.DevNum = "d1"
   119  	c.Assert(
   120  		func() { disks.MockMountPointDisksToPartitionMapping(m) },
   121  		PanicMatches,
   122  		`mocked disks .* and .* have the same DevNum \(d1\) but are not the same object`,
   123  	)
   124  
   125  	// mocking with just one disk at multiple mount points works too
   126  	m2 := map[disks.Mountpoint]*disks.MockDiskMapping{
   127  		{Mountpoint: "mount1"}: d1,
   128  		{Mountpoint: "mount2"}: d1,
   129  	}
   130  	r = disks.MockMountPointDisksToPartitionMapping(m2)
   131  	defer r()
   132  }
   133  
   134  func (s *mockDiskSuite) TestMockMountPointDisksToPartitionMappingVerifiesConsistency(c *C) {
   135  	d1 := &disks.MockDiskMapping{
   136  		FilesystemLabelToPartUUID: map[string]string{
   137  			"label1": "part1",
   138  		},
   139  		DiskHasPartitions: true,
   140  		DevNum:            "d1",
   141  	}
   142  
   143  	// a mountpoint mapping where the same mountpoint has different options for
   144  	// the source mountpoint
   145  	m := map[disks.Mountpoint]*disks.MockDiskMapping{
   146  		{Mountpoint: "mount1", IsDecryptedDevice: false}: d1,
   147  		{Mountpoint: "mount1", IsDecryptedDevice: true}:  d1,
   148  	}
   149  
   150  	// mocking shouldn't work
   151  	c.Assert(
   152  		func() { disks.MockMountPointDisksToPartitionMapping(m) },
   153  		PanicMatches,
   154  		// use .* for true/false since iterating over map order is not defined
   155  		`mocked source mountpoint mount1 is duplicated with different options - previous option for IsDecryptedDevice was .*, current option is .*`,
   156  	)
   157  }
   158  
   159  func (s *mockDiskSuite) TestMockMountPointDisksToPartitionMapping(c *C) {
   160  	d1 := &disks.MockDiskMapping{
   161  		FilesystemLabelToPartUUID: map[string]string{
   162  			"label1": "part1",
   163  		},
   164  		PartitionLabelToPartUUID: map[string]string{
   165  			"part-label1": "part1",
   166  		},
   167  		DiskHasPartitions: true,
   168  		DevNum:            "d1",
   169  	}
   170  
   171  	d2 := &disks.MockDiskMapping{
   172  		FilesystemLabelToPartUUID: map[string]string{
   173  			"label2": "part2",
   174  		},
   175  		PartitionLabelToPartUUID: map[string]string{
   176  			"part-label2": "part2",
   177  		},
   178  		DiskHasPartitions: true,
   179  		DevNum:            "d2",
   180  	}
   181  
   182  	r := disks.MockMountPointDisksToPartitionMapping(
   183  		map[disks.Mountpoint]*disks.MockDiskMapping{
   184  			{Mountpoint: "mount1"}: d1,
   185  			{Mountpoint: "mount2"}: d1,
   186  			{Mountpoint: "mount3"}: d2,
   187  		},
   188  	)
   189  	defer r()
   190  
   191  	// we can find the mock disk
   192  	foundDisk, err := disks.DiskFromMountPoint("mount1", nil)
   193  	c.Assert(err, IsNil)
   194  
   195  	// and it has filesystem labels
   196  	uuid, err := foundDisk.FindMatchingPartitionUUIDWithFsLabel("label1")
   197  	c.Assert(err, IsNil)
   198  	c.Assert(uuid, Equals, "part1")
   199  
   200  	// and partition labels
   201  	uuid, err = foundDisk.FindMatchingPartitionUUIDWithPartLabel("part-label1")
   202  	c.Assert(err, IsNil)
   203  	c.Assert(uuid, Equals, "part1")
   204  
   205  	// the same mount point is always from the same disk
   206  	matches, err := foundDisk.MountPointIsFromDisk("mount1", nil)
   207  	c.Assert(err, IsNil)
   208  	c.Assert(matches, Equals, true)
   209  
   210  	// mount2 goes to the same disk, as per the mapping above
   211  	matches, err = foundDisk.MountPointIsFromDisk("mount2", nil)
   212  	c.Assert(err, IsNil)
   213  	c.Assert(matches, Equals, true)
   214  
   215  	// mount3 does not however
   216  	matches, err = foundDisk.MountPointIsFromDisk("mount3", nil)
   217  	c.Assert(err, IsNil)
   218  	c.Assert(matches, Equals, false)
   219  
   220  	// a disk from mount3 is also able to be found
   221  	foundDisk2, err := disks.DiskFromMountPoint("mount3", nil)
   222  	c.Assert(err, IsNil)
   223  
   224  	// we can find label2 from mount3's disk
   225  	uuid, err = foundDisk2.FindMatchingPartitionUUIDWithFsLabel("label2")
   226  	c.Assert(err, IsNil)
   227  	c.Assert(uuid, Equals, "part2")
   228  
   229  	// and the partition label
   230  	uuid, err = foundDisk2.FindMatchingPartitionUUIDWithPartLabel("part-label2")
   231  	c.Assert(err, IsNil)
   232  	c.Assert(uuid, Equals, "part2")
   233  
   234  	// we can't find label1 from mount1's or mount2's disk
   235  	_, err = foundDisk2.FindMatchingPartitionUUIDWithFsLabel("label1")
   236  	c.Assert(err, ErrorMatches, "filesystem label \"label1\" not found")
   237  	c.Assert(err, DeepEquals, disks.PartitionNotFoundError{
   238  		SearchType:  "filesystem-label",
   239  		SearchQuery: "label1",
   240  	})
   241  
   242  	_, err = foundDisk2.FindMatchingPartitionUUIDWithPartLabel("part-label1")
   243  	c.Assert(err, ErrorMatches, "partition label \"part-label1\" not found")
   244  	c.Assert(err, DeepEquals, disks.PartitionNotFoundError{
   245  		SearchType:  "partition-label",
   246  		SearchQuery: "part-label1",
   247  	})
   248  
   249  	// mount1 and mount2 do not match mount3 disk
   250  	matches, err = foundDisk2.MountPointIsFromDisk("mount1", nil)
   251  	c.Assert(err, IsNil)
   252  	c.Assert(matches, Equals, false)
   253  	matches, err = foundDisk2.MountPointIsFromDisk("mount2", nil)
   254  	c.Assert(err, IsNil)
   255  	c.Assert(matches, Equals, false)
   256  }
   257  
   258  func (s *mockDiskSuite) TestMockMountPointDisksToPartitionMappingDecryptedDevices(c *C) {
   259  	d1 := &disks.MockDiskMapping{
   260  		FilesystemLabelToPartUUID: map[string]string{
   261  			"ubuntu-seed":     "ubuntu-seed-part",
   262  			"ubuntu-boot":     "ubuntu-boot-part",
   263  			"ubuntu-data-enc": "ubuntu-data-enc-part",
   264  		},
   265  		DiskHasPartitions: true,
   266  		DevNum:            "d1",
   267  	}
   268  
   269  	r := disks.MockMountPointDisksToPartitionMapping(
   270  		map[disks.Mountpoint]*disks.MockDiskMapping{
   271  			{Mountpoint: "/run/mnt/ubuntu-boot"}: d1,
   272  			{Mountpoint: "/run/mnt/ubuntu-seed"}: d1,
   273  			{
   274  				Mountpoint:        "/run/mnt/ubuntu-data",
   275  				IsDecryptedDevice: true,
   276  			}: d1,
   277  		},
   278  	)
   279  	defer r()
   280  
   281  	// first we get ubuntu-boot (which is not a decrypted device)
   282  	d, err := disks.DiskFromMountPoint("/run/mnt/ubuntu-boot", nil)
   283  	c.Assert(err, IsNil)
   284  
   285  	// next we find ubuntu-seed (also not decrypted)
   286  	label, err := d.FindMatchingPartitionUUIDWithFsLabel("ubuntu-seed")
   287  	c.Assert(err, IsNil)
   288  	c.Assert(label, Equals, "ubuntu-seed-part")
   289  
   290  	// then we find ubuntu-data-enc, which is not a decrypted device
   291  	label, err = d.FindMatchingPartitionUUIDWithFsLabel("ubuntu-data-enc")
   292  	c.Assert(err, IsNil)
   293  	c.Assert(label, Equals, "ubuntu-data-enc-part")
   294  
   295  	// and then finally ubuntu-data enc is from the same disk as ubuntu-boot
   296  	// with IsDecryptedDevice = true
   297  	opts := &disks.Options{IsDecryptedDevice: true}
   298  	matches, err := d.MountPointIsFromDisk("/run/mnt/ubuntu-data", opts)
   299  	c.Assert(err, IsNil)
   300  	c.Assert(matches, Equals, true)
   301  }