github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/gadget/ondisk_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019-2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package gadget_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/gadget"
    30  	"github.com/snapcore/snapd/gadget/quantity"
    31  	"github.com/snapcore/snapd/testutil"
    32  )
    33  
    34  type ondiskTestSuite struct {
    35  	testutil.BaseTest
    36  
    37  	dir string
    38  
    39  	gadgetRoot string
    40  }
    41  
    42  var _ = Suite(&ondiskTestSuite{})
    43  
    44  func (s *ondiskTestSuite) SetUpTest(c *C) {
    45  	s.BaseTest.SetUpTest(c)
    46  
    47  	s.dir = c.MkDir()
    48  
    49  	s.gadgetRoot = c.MkDir()
    50  	err := makeMockGadget(s.gadgetRoot, gadgetContent)
    51  	c.Assert(err, IsNil)
    52  }
    53  
    54  const mockSfdiskScriptBiosSeed = `
    55  >&2 echo "Some warning from sfdisk"
    56  echo '{
    57    "partitiontable": {
    58      "label": "gpt",
    59      "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA",
    60      "device": "/dev/node",
    61      "unit": "sectors",
    62      "firstlba": 34,
    63      "lastlba": 8388574,
    64      "partitions": [
    65        {
    66          "node": "/dev/node1",
    67          "start": 2048,
    68          "size": 2048,
    69          "type": "21686148-6449-6E6F-744E-656564454649",
    70          "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F",
    71          "name": "BIOS Boot"
    72        },
    73        {
    74          "node": "/dev/node2",
    75          "start": 4096,
    76          "size": 2457600,
    77          "type": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
    78          "uuid": "44C3D5C3-CAE1-4306-83E8-DF437ACDB32F",
    79          "name": "Recovery"
    80        }
    81      ]
    82    }
    83  }'`
    84  
    85  const mockLsblkScriptBiosSeed = `
    86  [ "$3" == "/dev/node1" ] && echo '{
    87      "blockdevices": [ {"name": "node1", "fstype": null, "label": null, "uuid": null, "mountpoint": null} ]
    88  }'
    89  [ "$3" == "/dev/node2" ] && echo '{
    90      "blockdevices": [ {"name": "node2", "fstype": "vfat", "label": "ubuntu-seed", "uuid": "A644-B807", "mountpoint": null} ]
    91  }'
    92  exit 0`
    93  
    94  func makeMockGadget(gadgetRoot, gadgetContent string) error {
    95  	if err := os.MkdirAll(filepath.Join(gadgetRoot, "meta"), 0755); err != nil {
    96  		return err
    97  	}
    98  	if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "meta", "gadget.yaml"), []byte(gadgetContent), 0644); err != nil {
    99  		return err
   100  	}
   101  	if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "pc-boot.img"), []byte("pc-boot.img content"), 0644); err != nil {
   102  		return err
   103  	}
   104  	if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "pc-core.img"), []byte("pc-core.img content"), 0644); err != nil {
   105  		return err
   106  	}
   107  	if err := ioutil.WriteFile(filepath.Join(gadgetRoot, "grubx64.efi"), []byte("grubx64.efi content"), 0644); err != nil {
   108  		return err
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  const gadgetContent = `volumes:
   115    pc:
   116      bootloader: grub
   117      structure:
   118        - name: mbr
   119          type: mbr
   120          size: 440
   121          content:
   122            - image: pc-boot.img
   123        - name: BIOS Boot
   124          type: DA,21686148-6449-6E6F-744E-656564454649
   125          size: 1M
   126          offset: 1M
   127          offset-write: mbr+92
   128          content:
   129            - image: pc-core.img
   130        - name: Recovery
   131          role: system-seed
   132          filesystem: vfat
   133          # UEFI will boot the ESP partition by default first
   134          type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B
   135          size: 1200M
   136          content:
   137            - source: grubx64.efi
   138              target: EFI/boot/grubx64.efi
   139        - name: Save
   140          role: system-save
   141          filesystem: ext4
   142          type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4
   143          size: 128M
   144        - name: Writable
   145          role: system-data
   146          filesystem: ext4
   147          type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4
   148          size: 1200M
   149  `
   150  
   151  func (s *ondiskTestSuite) TestDeviceInfoGPT(c *C) {
   152  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskScriptBiosSeed)
   153  	defer cmdSfdisk.Restore()
   154  
   155  	cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkScriptBiosSeed)
   156  	defer cmdLsblk.Restore()
   157  
   158  	cmdBlockdev := testutil.MockCommand(c, "blockdev", `
   159  if [ "$1" == --getss ]; then
   160  	# sector size
   161  	echo 512
   162  	exit 0
   163  fi
   164  echo "unexpected cmdline opts: $*"
   165  exit 1
   166  	`)
   167  	defer cmdBlockdev.Restore()
   168  
   169  	dl, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   170  	c.Assert(err, IsNil)
   171  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   172  		{"sfdisk", "--json", "/dev/node"},
   173  	})
   174  	c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{
   175  		{"lsblk", "--fs", "--json", "/dev/node1"},
   176  		{"lsblk", "--fs", "--json", "/dev/node2"},
   177  	})
   178  	c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{
   179  		{"blockdev", "--getss", "/dev/node"},
   180  	})
   181  	c.Assert(err, IsNil)
   182  	c.Assert(dl, DeepEquals, &gadget.OnDiskVolume{
   183  		Device:     "/dev/node",
   184  		ID:         "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA",
   185  		Schema:     "gpt",
   186  		SectorSize: quantity.Size(512),
   187  		Size:       quantity.Size(8388575 * 512),
   188  		Structure: []gadget.OnDiskStructure{
   189  			{
   190  				LaidOutStructure: gadget.LaidOutStructure{
   191  					VolumeStructure: &gadget.VolumeStructure{
   192  						Name:       "BIOS Boot",
   193  						Size:       0x100000,
   194  						Label:      "",
   195  						Type:       "21686148-6449-6E6F-744E-656564454649",
   196  						Filesystem: "",
   197  					},
   198  					StartOffset: 0x100000,
   199  					Index:       1,
   200  				},
   201  				Node: "/dev/node1",
   202  			},
   203  			{
   204  				LaidOutStructure: gadget.LaidOutStructure{
   205  					VolumeStructure: &gadget.VolumeStructure{
   206  						Name:       "Recovery",
   207  						Size:       0x4b000000,
   208  						Label:      "ubuntu-seed",
   209  						Type:       "C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
   210  						Filesystem: "vfat",
   211  					},
   212  					StartOffset: 0x200000,
   213  					Index:       2,
   214  				},
   215  				Node: "/dev/node2",
   216  			},
   217  		},
   218  	})
   219  }
   220  
   221  func (s *ondiskTestSuite) TestDeviceInfoGPT4096SectorSize(c *C) {
   222  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskScriptBiosSeed)
   223  	defer cmdSfdisk.Restore()
   224  
   225  	cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkScriptBiosSeed)
   226  	defer cmdLsblk.Restore()
   227  
   228  	cmdBlockdev := testutil.MockCommand(c, "blockdev", `
   229  if [ "$1" == --getss ]; then
   230  	# sector size
   231  	echo 4096
   232  	exit 0
   233  fi
   234  echo "unexpected cmdline opts: $*"
   235  exit 1
   236  	`)
   237  	defer cmdBlockdev.Restore()
   238  
   239  	dl, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   240  	c.Assert(err, IsNil)
   241  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   242  		{"sfdisk", "--json", "/dev/node"},
   243  	})
   244  	c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{
   245  		{"lsblk", "--fs", "--json", "/dev/node1"},
   246  		{"lsblk", "--fs", "--json", "/dev/node2"},
   247  	})
   248  	c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{
   249  		{"blockdev", "--getss", "/dev/node"},
   250  	})
   251  	c.Assert(err, IsNil)
   252  	c.Assert(dl, DeepEquals, &gadget.OnDiskVolume{
   253  		Device:     "/dev/node",
   254  		ID:         "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA",
   255  		Schema:     "gpt",
   256  		SectorSize: quantity.Size(4096),
   257  		Size:       quantity.Size(8388575 * 4096),
   258  		Structure: []gadget.OnDiskStructure{
   259  			{
   260  				LaidOutStructure: gadget.LaidOutStructure{
   261  					VolumeStructure: &gadget.VolumeStructure{
   262  						Name:       "BIOS Boot",
   263  						Size:       0x800000,
   264  						Label:      "",
   265  						Type:       "21686148-6449-6E6F-744E-656564454649",
   266  						Filesystem: "",
   267  					},
   268  					StartOffset: 0x800000,
   269  					Index:       1,
   270  				},
   271  				Node: "/dev/node1",
   272  			},
   273  			{
   274  				LaidOutStructure: gadget.LaidOutStructure{
   275  					VolumeStructure: &gadget.VolumeStructure{
   276  						Name:       "Recovery",
   277  						Size:       0x258000000,
   278  						Label:      "ubuntu-seed",
   279  						Type:       "C12A7328-F81F-11D2-BA4B-00A0C93EC93B",
   280  						Filesystem: "vfat",
   281  					},
   282  					StartOffset: 0x1000000,
   283  					Index:       2,
   284  				},
   285  				Node: "/dev/node2",
   286  			},
   287  		},
   288  	})
   289  }
   290  
   291  func (s *ondiskTestSuite) TestDeviceInfoMBR(c *C) {
   292  	const mockSfdiskWithMBR = `
   293  >&2 echo "Some warning from sfdisk"
   294  echo '{
   295     "partitiontable": {
   296        "label": "dos",
   297        "device": "/dev/node",
   298        "unit": "sectors",
   299        "partitions": [
   300           {"node": "/dev/node1", "start": 4096, "size": 2457600, "type": "c"},
   301           {"node": "/dev/node2", "start": 2461696, "size": 1048576, "type": "d"},
   302           {"node": "/dev/node3", "start": 3510272, "size": 1048576, "type": "d"},
   303           {"node": "/dev/node4", "start": 4558848, "size": 1048576, "type": "d"}
   304        ]
   305     }
   306  }'`
   307  	const mockLsblkForMBR = `
   308  [ "$3" == "/dev/node1" ] && echo '{
   309      "blockdevices": [ {"name": "node1", "fstype": "vfat", "label": "ubuntu-seed", "uuid": "A644-B807", "mountpoint": null} ]
   310  }'
   311  [ "$3" == "/dev/node2" ] && echo '{
   312      "blockdevices": [ {"name": "node2", "fstype": "vfat", "label": "ubuntu-boot", "uuid": "A644-B808", "mountpoint": null} ]
   313  }'
   314  [ "$3" == "/dev/node3" ] && echo '{
   315      "blockdevices": [ {"name": "node3", "fstype": "ext4", "label": "ubuntu-save", "mountpoint": null} ]
   316  }'
   317  [ "$3" == "/dev/node4" ] && echo '{
   318      "blockdevices": [ {"name": "node3", "fstype": "ext4", "label": "ubuntu-data", "mountpoint": null} ]
   319  }'
   320  exit 0`
   321  
   322  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskWithMBR)
   323  	defer cmdSfdisk.Restore()
   324  
   325  	cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkForMBR)
   326  	defer cmdLsblk.Restore()
   327  
   328  	cmdBlockdev := testutil.MockCommand(c, "blockdev", `
   329  if [ "$1" == --getss ]; then
   330  	# sector size
   331  	echo 512
   332  	exit 0
   333  elif [ "$1" == --getsz ]; then
   334  # disk size in 512-byte sectors
   335  	echo 12345670
   336  	exit 0
   337  fi
   338  echo "unexpected cmdline opts: $*"
   339  exit 1
   340  	`)
   341  	defer cmdBlockdev.Restore()
   342  
   343  	dl, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   344  	c.Assert(err, IsNil)
   345  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   346  		{"sfdisk", "--json", "/dev/node"},
   347  	})
   348  	c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{
   349  		{"lsblk", "--fs", "--json", "/dev/node1"},
   350  		{"lsblk", "--fs", "--json", "/dev/node2"},
   351  		{"lsblk", "--fs", "--json", "/dev/node3"},
   352  		{"lsblk", "--fs", "--json", "/dev/node4"},
   353  	})
   354  	c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{
   355  		{"blockdev", "--getss", "/dev/node"},
   356  		{"blockdev", "--getsz", "/dev/node"},
   357  	})
   358  	c.Assert(err, IsNil)
   359  
   360  	c.Assert(dl, DeepEquals, &gadget.OnDiskVolume{
   361  		Device:     "/dev/node",
   362  		Schema:     "dos",
   363  		SectorSize: quantity.Size(512),
   364  		Size:       quantity.Size(12345670 * 512),
   365  		Structure: []gadget.OnDiskStructure{
   366  			{
   367  				LaidOutStructure: gadget.LaidOutStructure{
   368  					VolumeStructure: &gadget.VolumeStructure{
   369  						Size:       2457600 * 512,
   370  						Label:      "ubuntu-seed",
   371  						Type:       "0C",
   372  						Filesystem: "vfat",
   373  					},
   374  					StartOffset: 4096 * 512,
   375  					Index:       1,
   376  				},
   377  				Node: "/dev/node1",
   378  			},
   379  			{
   380  				LaidOutStructure: gadget.LaidOutStructure{
   381  					VolumeStructure: &gadget.VolumeStructure{
   382  						Size:       1048576 * 512,
   383  						Label:      "ubuntu-boot",
   384  						Type:       "0D",
   385  						Filesystem: "vfat",
   386  					},
   387  					StartOffset: (4096 + 2457600) * 512,
   388  					Index:       2,
   389  				},
   390  				Node: "/dev/node2",
   391  			},
   392  			{
   393  				LaidOutStructure: gadget.LaidOutStructure{
   394  					VolumeStructure: &gadget.VolumeStructure{
   395  						Size:       1048576 * 512,
   396  						Label:      "ubuntu-save",
   397  						Type:       "0D",
   398  						Filesystem: "ext4",
   399  					},
   400  					StartOffset: (4096 + 2457600 + 1048576) * 512,
   401  					Index:       3,
   402  				},
   403  				Node: "/dev/node3",
   404  			},
   405  			{
   406  				LaidOutStructure: gadget.LaidOutStructure{
   407  					VolumeStructure: &gadget.VolumeStructure{
   408  						Size:       1048576 * 512,
   409  						Label:      "ubuntu-data",
   410  						Type:       "0D",
   411  						Filesystem: "ext4",
   412  					},
   413  					StartOffset: (4096 + 2457600 + 1048576 + 1048576) * 512,
   414  					Index:       4,
   415  				},
   416  				Node: "/dev/node4",
   417  			},
   418  		},
   419  	})
   420  }
   421  
   422  func (s *ondiskTestSuite) TestDeviceInfoMBR4096SectorSize(c *C) {
   423  	const mockSfdiskWithMBR = `
   424  >&2 echo "Some warning from sfdisk"
   425  echo '{
   426     "partitiontable": {
   427        "label": "dos",
   428        "device": "/dev/node",
   429        "unit": "sectors",
   430        "partitions": [
   431  		{"node": "/dev/node1", "start":256, "size":2560, "type": "c"},
   432  		{"node": "/dev/node2", "start":2816, "size":2560, "type": "d"},
   433  		{"node": "/dev/node3", "start":5376, "size":128000, "type": "d"},
   434  		{"node": "/dev/node4", "start":133376, "size":6202079, "type": "d"}
   435  	 ]
   436     }
   437  }'`
   438  	const mockLsblkForMBR = `
   439  [ "$3" == "/dev/node1" ] && echo '{
   440      "blockdevices": [ {"name": "node1", "fstype": "vfat", "label": "ubuntu-seed", "uuid": "A644-B807", "mountpoint": null} ]
   441  }'
   442  [ "$3" == "/dev/node2" ] && echo '{
   443      "blockdevices": [ {"name": "node2", "fstype": "vfat", "label": "ubuntu-boot", "uuid": "A644-B808", "mountpoint": null} ]
   444  }'
   445  [ "$3" == "/dev/node3" ] && echo '{
   446      "blockdevices": [ {"name": "node3", "fstype": "ext4", "label": "ubuntu-save", "mountpoint": null} ]
   447  }'
   448  [ "$3" == "/dev/node4" ] && echo '{
   449      "blockdevices": [ {"name": "node3", "fstype": "ext4", "label": "ubuntu-data", "mountpoint": null} ]
   450  }'
   451  exit 0`
   452  
   453  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskWithMBR)
   454  	defer cmdSfdisk.Restore()
   455  
   456  	cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkForMBR)
   457  	defer cmdLsblk.Restore()
   458  
   459  	cmdBlockdev := testutil.MockCommand(c, "blockdev", `
   460  if [ "$1" == --getss ]; then
   461  	# sector size
   462  	echo 4096
   463  	exit 0
   464  elif [ "$1" == --getsz ]; then
   465  	# disk size in 512-byte sectors
   466  	echo 50683904
   467  	exit 0
   468  fi
   469  echo "unexpected cmdline opts: $*"
   470  exit 1
   471  	`)
   472  	defer cmdBlockdev.Restore()
   473  
   474  	dl, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   475  	c.Assert(err, IsNil)
   476  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   477  		{"sfdisk", "--json", "/dev/node"},
   478  	})
   479  	c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{
   480  		{"lsblk", "--fs", "--json", "/dev/node1"},
   481  		{"lsblk", "--fs", "--json", "/dev/node2"},
   482  		{"lsblk", "--fs", "--json", "/dev/node3"},
   483  		{"lsblk", "--fs", "--json", "/dev/node4"},
   484  	})
   485  	c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{
   486  		{"blockdev", "--getss", "/dev/node"},
   487  		{"blockdev", "--getsz", "/dev/node"},
   488  	})
   489  	c.Assert(err, IsNil)
   490  
   491  	c.Assert(dl, DeepEquals, &gadget.OnDiskVolume{
   492  		Device:     "/dev/node",
   493  		Schema:     "dos",
   494  		SectorSize: quantity.Size(4096),
   495  		Size:       quantity.Size(6335488 * 4096),
   496  		Structure: []gadget.OnDiskStructure{
   497  			{
   498  				LaidOutStructure: gadget.LaidOutStructure{
   499  					VolumeStructure: &gadget.VolumeStructure{
   500  						Size:       2560 * 4096,
   501  						Label:      "ubuntu-seed",
   502  						Type:       "0C",
   503  						Filesystem: "vfat",
   504  					},
   505  					StartOffset: 256 * 4096,
   506  					Index:       1,
   507  				},
   508  				Node: "/dev/node1",
   509  			},
   510  			{
   511  				LaidOutStructure: gadget.LaidOutStructure{
   512  					VolumeStructure: &gadget.VolumeStructure{
   513  						Size:       2560 * 4096,
   514  						Label:      "ubuntu-boot",
   515  						Type:       "0D",
   516  						Filesystem: "vfat",
   517  					},
   518  					StartOffset: (256 + 2560) * 4096,
   519  					Index:       2,
   520  				},
   521  				Node: "/dev/node2",
   522  			},
   523  			{
   524  				LaidOutStructure: gadget.LaidOutStructure{
   525  					VolumeStructure: &gadget.VolumeStructure{
   526  						Size:       128000 * 4096,
   527  						Label:      "ubuntu-save",
   528  						Type:       "0D",
   529  						Filesystem: "ext4",
   530  					},
   531  					StartOffset: (256 + 2560 + 2560) * 4096,
   532  					Index:       3,
   533  				},
   534  				Node: "/dev/node3",
   535  			},
   536  			{
   537  				LaidOutStructure: gadget.LaidOutStructure{
   538  					VolumeStructure: &gadget.VolumeStructure{
   539  						Size:       6202079 * 4096,
   540  						Label:      "ubuntu-data",
   541  						Type:       "0D",
   542  						Filesystem: "ext4",
   543  					},
   544  					StartOffset: (256 + 2560 + 2560 + 128000) * 4096,
   545  					Index:       4,
   546  				},
   547  				Node: "/dev/node4",
   548  			},
   549  		},
   550  	})
   551  }
   552  
   553  func (s *ondiskTestSuite) TestDeviceInfoNotSectors(c *C) {
   554  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", `echo '{
   555     "partitiontable": {
   556        "label": "gpt",
   557        "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA",
   558        "device": "/dev/node",
   559        "unit": "not_sectors",
   560        "firstlba": 34,
   561        "lastlba": 8388574,
   562        "partitions": [
   563           {"node": "/dev/node1", "start": 2048, "size": 2048, "type": "21686148-6449-6E6F-744E-656564454649", "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F", "name": "BIOS Boot"}
   564        ]
   565     }
   566  }'`)
   567  	defer cmdSfdisk.Restore()
   568  
   569  	_, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   570  	c.Assert(err, ErrorMatches, "cannot position partitions: unknown unit .*")
   571  }
   572  
   573  func (s *ondiskTestSuite) TestDeviceInfoSectorSizeNotMultiple512Unhappy(c *C) {
   574  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", `echo '{
   575     "partitiontable": {
   576        "label": "gpt",
   577        "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA",
   578        "device": "/dev/node",
   579        "unit": "sectors",
   580        "firstlba": 34,
   581        "lastlba": 8388574,
   582        "partitions": [
   583           {"node": "/dev/node1", "start": 2048, "size": 2048, "type": "21686148-6449-6E6F-744E-656564454649", "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F", "name": "BIOS Boot"}
   584        ]
   585     }
   586  }'`)
   587  	defer cmdSfdisk.Restore()
   588  
   589  	cmdBlockdev := testutil.MockCommand(c, "blockdev", `
   590  if [ "$1" == --getss ]; then
   591     # sector size
   592     echo 513
   593     exit 0
   594  fi
   595  echo "unexpected cmdline opts: $*"
   596  exit 1
   597  `)
   598  	defer cmdBlockdev.Restore()
   599  
   600  	_, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   601  	c.Assert(err, ErrorMatches, `cannot calculate structure size: sector size \(513\) is not a multiple of 512`)
   602  
   603  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   604  		{"sfdisk", "--json", "/dev/node"},
   605  	})
   606  
   607  	c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{
   608  		{"blockdev", "--getss", "/dev/node"},
   609  	})
   610  
   611  }
   612  
   613  func (s *ondiskTestSuite) TestDeviceInfoFilesystemInfoError(c *C) {
   614  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", `echo '{
   615     "partitiontable": {
   616        "label": "gpt",
   617        "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA",
   618        "device": "/dev/node",
   619        "unit": "sectors",
   620        "firstlba": 34,
   621        "lastlba": 8388574,
   622        "partitions": [
   623           {"node": "/dev/node1", "start": 2048, "size": 2048, "type": "21686148-6449-6E6F-744E-656564454649", "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F", "name": "BIOS Boot"}
   624        ]
   625     }
   626  }'`)
   627  	defer cmdSfdisk.Restore()
   628  
   629  	cmdBlockdev := testutil.MockCommand(c, "blockdev", `
   630  if [ "$1" == --getss ]; then
   631     # sector size
   632     echo 512
   633     exit 0
   634  fi
   635  echo "unexpected cmdline opts: $*"
   636  exit 1
   637  `)
   638  	defer cmdBlockdev.Restore()
   639  
   640  	cmdLsblk := testutil.MockCommand(c, "lsblk", "echo lsblk error; exit 1")
   641  	defer cmdLsblk.Restore()
   642  
   643  	_, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   644  	c.Assert(err, ErrorMatches, "cannot obtain filesystem information: lsblk error")
   645  
   646  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   647  		{"sfdisk", "--json", "/dev/node"},
   648  	})
   649  
   650  	c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{
   651  		{"blockdev", "--getss", "/dev/node"},
   652  	})
   653  
   654  	c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{
   655  		{"lsblk", "--fs", "--json", "/dev/node1"},
   656  	})
   657  }
   658  
   659  func (s *ondiskTestSuite) TestDeviceInfoJsonError(c *C) {
   660  	cmd := testutil.MockCommand(c, "sfdisk", `echo 'This is not a json'`)
   661  	defer cmd.Restore()
   662  
   663  	dl, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   664  	c.Assert(err, ErrorMatches, "cannot parse sfdisk output: invalid .*")
   665  	c.Assert(dl, IsNil)
   666  }
   667  
   668  func (s *ondiskTestSuite) TestDeviceInfoError(c *C) {
   669  	cmd := testutil.MockCommand(c, "sfdisk", "echo 'sfdisk: not found'; exit 127")
   670  	defer cmd.Restore()
   671  
   672  	dl, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   673  	c.Assert(err, ErrorMatches, "sfdisk: not found")
   674  	c.Assert(dl, IsNil)
   675  }
   676  
   677  func (s *ondiskTestSuite) TestUpdatePartitionList(c *C) {
   678  	const mockSfdiskScriptBios = `
   679  >&2 echo "Some warning from sfdisk"
   680  echo '{
   681    "partitiontable": {
   682      "label": "gpt",
   683      "id": "9151F25B-CDF0-48F1-9EDE-68CBD616E2CA",
   684      "device": "/dev/node",
   685      "unit": "sectors",
   686      "firstlba": 34,
   687      "lastlba": 8388574,
   688      "partitions": [
   689        {
   690          "node": "/dev/node1",
   691          "start": 2048,
   692          "size": 2048,
   693          "type": "21686148-6449-6E6F-744E-656564454649",
   694          "uuid": "2E59D969-52AB-430B-88AC-F83873519F6F",
   695          "name": "BIOS Boot"
   696        }
   697      ]
   698    }
   699  }'`
   700  
   701  	const mockLsblkScriptBios = `
   702  [ "$3" == "/dev/node1" ] && echo '{
   703      "blockdevices": [ {"name": "node1", "fstype": null, "label": null, "uuid": null, "mountpoint": null} ]
   704  }'
   705  exit 0`
   706  
   707  	// sector size is same for all calls
   708  	cmdBlockdev := testutil.MockCommand(c, "blockdev", `
   709  if [ "$1" == --getss ]; then
   710     # sector size
   711     echo 512
   712     exit 0
   713  fi
   714  echo "unexpected cmdline opts: $*"
   715  exit 1
   716  `)
   717  	defer cmdBlockdev.Restore()
   718  
   719  	// start with a single partition
   720  	cmdSfdisk := testutil.MockCommand(c, "sfdisk", mockSfdiskScriptBios)
   721  	defer cmdSfdisk.Restore()
   722  
   723  	cmdLsblk := testutil.MockCommand(c, "lsblk", mockLsblkScriptBios)
   724  	defer cmdLsblk.Restore()
   725  
   726  	dl, err := gadget.OnDiskVolumeFromDevice("/dev/node")
   727  	c.Assert(err, IsNil)
   728  
   729  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   730  		{"sfdisk", "--json", "/dev/node"},
   731  	})
   732  
   733  	c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{
   734  		{"lsblk", "--fs", "--json", "/dev/node1"},
   735  	})
   736  
   737  	c.Assert(len(dl.Structure), Equals, 1)
   738  	c.Assert(dl.Structure[0].Node, Equals, "/dev/node1")
   739  
   740  	// add a partition
   741  	cmdSfdisk = testutil.MockCommand(c, "sfdisk", mockSfdiskScriptBiosSeed)
   742  	defer cmdSfdisk.Restore()
   743  
   744  	cmdLsblk = testutil.MockCommand(c, "lsblk", mockLsblkScriptBiosSeed)
   745  	defer cmdLsblk.Restore()
   746  
   747  	// update the partition list
   748  	err = gadget.UpdatePartitionList(dl)
   749  	c.Assert(err, IsNil)
   750  
   751  	c.Assert(cmdBlockdev.Calls(), DeepEquals, [][]string{
   752  		{"blockdev", "--getss", "/dev/node"},
   753  		{"blockdev", "--getss", "/dev/node"},
   754  	})
   755  
   756  	c.Assert(cmdSfdisk.Calls(), DeepEquals, [][]string{
   757  		{"sfdisk", "--json", "/dev/node"},
   758  	})
   759  
   760  	c.Assert(cmdLsblk.Calls(), DeepEquals, [][]string{
   761  		{"lsblk", "--fs", "--json", "/dev/node1"},
   762  		{"lsblk", "--fs", "--json", "/dev/node2"},
   763  	})
   764  
   765  	// check if the partition list was updated
   766  	c.Assert(len(dl.Structure), Equals, 2)
   767  	c.Assert(dl.Structure[0].Node, Equals, "/dev/node1")
   768  	c.Assert(dl.Structure[1].Node, Equals, "/dev/node2")
   769  }
   770  
   771  func (s *ondiskTestSuite) TestFilesystemInfo(c *C) {
   772  	cmd := testutil.MockCommand(c, "lsblk", `echo '{
   773     "blockdevices": [
   774        {"name": "loop8p2", "fstype": "vfat", "label": "ubuntu-seed", "uuid": "C1F4-CE43", "mountpoint": null}
   775     ]
   776  }'`)
   777  	defer cmd.Restore()
   778  
   779  	info, err := gadget.FilesystemInfo("/dev/node")
   780  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
   781  		{"lsblk", "--fs", "--json", "/dev/node"},
   782  	})
   783  	c.Assert(err, IsNil)
   784  	c.Assert(len(info.BlockDevices), Equals, 1)
   785  	bd := info.BlockDevices[0]
   786  	c.Assert(bd.Name, Equals, "loop8p2")
   787  	c.Assert(bd.FSType, Equals, "vfat")
   788  	c.Assert(bd.Label, Equals, "ubuntu-seed")
   789  	c.Assert(bd.UUID, Equals, "C1F4-CE43")
   790  }
   791  
   792  func (s *ondiskTestSuite) TestFilesystemInfoJsonError(c *C) {
   793  	cmd := testutil.MockCommand(c, "lsblk", `echo 'This is not a json'`)
   794  	defer cmd.Restore()
   795  
   796  	info, err := gadget.FilesystemInfo("/dev/node")
   797  	c.Assert(err, ErrorMatches, "cannot parse lsblk output: invalid .*")
   798  	c.Assert(info, IsNil)
   799  }
   800  
   801  func (s *ondiskTestSuite) TestFilesystemInfoError(c *C) {
   802  	cmd := testutil.MockCommand(c, "lsblk", "echo 'lsblk: not found'; exit 127")
   803  	defer cmd.Restore()
   804  
   805  	info, err := gadget.FilesystemInfo("/dev/node")
   806  	c.Assert(err, ErrorMatches, "lsblk: not found")
   807  	c.Assert(info, IsNil)
   808  }