Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

findpartitions rewrite #9594

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions cmd/snap-bootstrap/cmd_initramfs_mounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ func mountPartitionMatchingKernelDisk(dir, fallbacklabel string) error {
// to use by-uuid or by-id
partSrc := filepath.Join("/dev/disk/by-partuuid", partuuid)
if err != nil {
// when booted on UEFI this should be fatal!
// no luck, try mounting by label instead
partSrc = filepath.Join("/dev/disk/by-label", fallbacklabel)
}
Expand Down Expand Up @@ -492,7 +493,7 @@ func maybeMountSave(disk disks.Disk, rootdir string, encrypted bool, mountOpts *
return true, fmt.Errorf("cannot unlock ubuntu-save volume: %v", err)
}
} else {
partUUID, err := disk.FindMatchingPartitionUUID("ubuntu-save")
saveDevice, err := disk.FindMatchingPartition("ubuntu-save")
if err != nil {
if _, ok := err.(disks.FilesystemLabelNotFoundError); ok {
// this is ok, ubuntu-save may not exist for
Expand All @@ -501,7 +502,6 @@ func maybeMountSave(disk disks.Disk, rootdir string, encrypted bool, mountOpts *
}
return false, err
}
saveDevice = filepath.Join("/dev/disk/by-partuuid", partUUID)
}
if err := doSystemdMount(saveDevice, boot.InitramfsUbuntuSaveDir, mountOpts); err != nil {
return true, err
Expand All @@ -525,7 +525,7 @@ func generateMountsModeRun(mst *initramfsMountsState) error {
// 2. mount ubuntu-seed
// use the disk we mounted ubuntu-boot from as a reference to find
// ubuntu-seed and mount it
partUUID, err := disk.FindMatchingPartitionUUID("ubuntu-seed")
partition, err := disk.FindMatchingPartition("ubuntu-seed")
if err != nil {
return err
}
Expand All @@ -538,7 +538,7 @@ func generateMountsModeRun(mst *initramfsMountsState) error {
fsckSystemdOpts := &systemdMountOptions{
NeedsFsck: true,
}
if err := doSystemdMount(fmt.Sprintf("/dev/disk/by-partuuid/%s", partUUID), boot.InitramfsUbuntuSeedDir, fsckSystemdOpts); err != nil {
if err := doSystemdMount(partition, boot.InitramfsUbuntuSeedDir, fsckSystemdOpts); err != nil {
return err
}

Expand Down
164 changes: 81 additions & 83 deletions osutil/disks/disks.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type Disk interface {
// If the filesystem label was not found on the disk, and no other errors
// were encountered, a FilesystemLabelNotFoundError will be returned.
FindMatchingPartitionUUID(string) (string, error)
FindMatchingPartition(string) (string, error)

// MountPointIsFromDisk returns whether the specified mountpoint corresponds
// to a partition on the disk. Note that this only considers partitions
Expand All @@ -97,38 +98,12 @@ type Disk interface {
HasPartitions() bool
}

func parseDeviceMajorMinor(s string) (int, int, error) {
errMsg := fmt.Errorf("invalid device number format: (expected <int>:<int>)")
devNums := strings.SplitN(s, ":", 2)
if len(devNums) != 2 {
return 0, 0, errMsg
}
maj, err := strconv.Atoi(devNums[0])
if err != nil {
return 0, 0, errMsg
}
min, err := strconv.Atoi(devNums[1])
if err != nil {
return 0, 0, errMsg
}
return maj, min, nil
}

var udevadmProperties = func(device string) ([]byte, error) {
// TODO: maybe combine with gadget interfaces hotplug code where the udev
// db is parsed?
cmd := exec.Command("udevadm", "info", "--query", "property", "--name", device)
return cmd.CombinedOutput()
}

func udevProperties(device string) (map[string]string, error) {
out, err := udevadmProperties(device)
func ueventProperties(sysfspath string) (map[string]string, error) {
out, err := os.Open(filepath.Join(sysfspath, "uevent"))
if err != nil {
return nil, osutil.OutputErr(out, err)
}
r := bytes.NewBuffer(out)

return parseUdevProperties(r)
return parseUdevProperties(out)
}

func parseUdevProperties(r io.Reader) (map[string]string, error) {
Expand Down Expand Up @@ -192,16 +167,21 @@ func diskFromMountPointImpl(mountpoint string, opts *Options) (*disk, error) {
return nil, fmt.Errorf("cannot find mountpoint %q", mountpoint)
}

// now we have the partition for this mountpoint, we need to tie that back
// to a disk with a major minor, so query udev with the mount source path
// of the mountpoint for properties
props, err := udevProperties(partMountPointSource)
if err != nil && props == nil {
// only fail here if props is nil, if it's available we validate it
// below
return nil, fmt.Errorf("cannot find disk for partition %s: %v", partMountPointSource, err)
sysfsPath, err := filepath.EvalSymlinks(filepath.Join("/sys/dev/block", d.Dev()))
if err != nil {
return nil, fmt.Errorf(errFmt, partMountPointSource, err)
}

props, err := ueventProperties(sysfsPath)
if err != nil {
return nil, fmt.Errorf(errFmt, partMountPointSource, err)
}

// read uevent if you want kernel props
// if dm is present, partition is in realpath slaves/*
// if uevent says DEVTYPE=partition, then parent '../' is the parent device
// can read major of the parent there.

if opts != nil && opts.IsDecryptedDevice {
// verify that the mount point is indeed a mapper device, it should:
// 1. have DEVTYPE == disk from udev
Expand Down Expand Up @@ -233,12 +213,12 @@ func diskFromMountPointImpl(mountpoint string, opts *Options) (*disk, error) {
// be missing from the initrd previously, and are not
// available at all during userspace on UC20 for some reason
errFmt := "mountpoint source %s is not a decrypted device: could not read device mapper metadata: %v"
dmUUID, err := ioutil.ReadFile(filepath.Join(devBlockDir, d.Dev(), "dm", "uuid"))
dmUUID, err := ioutil.ReadFile(filepath.Join(sysfsPath, "dm", "uuid"))
if err != nil {
return nil, fmt.Errorf(errFmt, partMountPointSource, err)
}

dmName, err := ioutil.ReadFile(filepath.Join(devBlockDir, d.Dev(), "dm", "name"))
dmName, err := ioutil.ReadFile(filepath.Join(sysfsPath, "dm", "name"))
if err != nil {
return nil, fmt.Errorf(errFmt, partMountPointSource, err)
}
Expand All @@ -260,58 +240,47 @@ func diskFromMountPointImpl(mountpoint string, opts *Options) (*disk, error) {
return nil, fmt.Errorf("cannot verify disk: partition %s does not have a valid luks uuid format: %s", d.Dev(), dmUUIDSafe)
}

// the uuid is the first and only submatch, but it is not in the same
// format exactly as we want to use, namely it is missing all of the "-"
// characters in a typical uuid, i.e. it is of the form:
// ae6e79de00a9406f80ee64ba7c1966bb but we want it to be like:
// ae6e79de-00a9-406f-80ee-64ba7c1966bb so we need to add in 4 "-"
// characters
compactUUID := string(matches[1])
canonicalUUID := fmt.Sprintf(
"%s-%s-%s-%s-%s",
compactUUID[0:8],
compactUUID[8:12],
compactUUID[12:16],
compactUUID[16:20],
compactUUID[20:],
)

// now finally, we need to use this uuid, which is the device uuid of
// the actual physical encrypted partition to get the path, which will
// be something like /dev/vda4, etc.
byUUIDPath := filepath.Join("/dev/disk/by-uuid", canonicalUUID)
props, err = udevProperties(byUUIDPath)
// Above has checked that this is dm mapper, encrypted
// device. To find the partition one must inspect the symlink
// in /sys/bloc/dm-0/slaves/nvme0n1p6 -> symlink to a
// partition dir, under a device.
// This is the kernel representation of where this mount came from
childrenDir := filepath.Join(sysfsPath, "slaves")
fil, err := ioutil.ReadDir(childrenDir)
if len(fil) != 1 || err != nil {
return nil, err
}
sysfsPath = filepath.EvalSymlinks(filepath.Join(childrenDir, fil[0].Name()))
if err != nil {
return nil, fmt.Errorf("cannot get udev properties for encrypted partition %s: %v", byUUIDPath, err)
return nil, err
}
}

// ID_PART_ENTRY_DISK will give us the major and minor of the disk that this
// partition originated from
if majorMinor, ok := props["ID_PART_ENTRY_DISK"]; ok {
maj, min, err := parseDeviceMajorMinor(majorMinor)
props, err = udevProperties(sysfsPath)
if err != nil {
// bad udev output?
return nil, fmt.Errorf("cannot find disk for partition %s, bad udev output: %v", partMountPointSource, err)
return nil, fmt.Errorf("cannot get udev properties for encrypted partition %s: %v", childPath, err)
}
d.major = maj
d.minor = min

// since the mountpoint device has a disk, the mountpoint source itself
// must be a partition from a disk, thus the disk has partitions
d.hasPartitions = true
return d, nil
}

// if we don't have ID_PART_ENTRY_DISK, the partition is probably a volume
// or other non-physical disk, so confirm that DEVTYPE == disk and return
// the maj/min for it
if devType, ok := props["DEVTYPE"]; ok {
if devType == "disk" {
return d, nil
switch devType {
case "disk":
case "partition":
d.hasPartitions = true
props, err = udevProperties(filepath.Join(sysfsPath, '..'))
if err != nil {
return nil, err
}
case default:
return nil, fmt.Errorf("unsupported DEVTYPE %q for mount point source %s", devType, partMountPointSource)
}
d.major, err = strconv.Atoi(props["MAJOR"])
if err != nil {
return nil, err
}
d.minor, err = strconv.Atoi(props["MINOR"])
if err != nil {
return nil, err
}
// unclear what other DEVTYPE's we should support for this function
return nil, fmt.Errorf("unsupported DEVTYPE %q for mount point source %s", devType, partMountPointSource)
return d, nil
}

return nil, fmt.Errorf("cannot find disk for partition %s, incomplete udev output", partMountPointSource)
Expand Down Expand Up @@ -404,6 +373,35 @@ func (d *disk) FindMatchingPartitionUUID(label string) (string, error) {
return "", FilesystemLabelNotFoundError{Label: label}
}

func (d *disk) FindMatchingPartition(label string) (string, error) {
sysfsPath, err := filepath.EvalSymlinks(filepath.Join("/sys/dev/block", d.Dev()))
if err != nil {
return "", FilesystemLabelNotFoundError{Label: label}
}

subdirs, err := ioutil.ReadDir(sysfsPath)
if err != nil {
return "", FilesystemLabelNotFoundError{Label: label}
}
for _, file := range files {
if file.IsDir() && (file.Mode()&os.ModeSymlink == 0) {
partitionPath := filepath.Join(sysfsPath, file.Name())
props, err := ueventProperties(partitionPath)
if err != nil {
return "", FilesystemLabelNotFoundError{Label: label}
}
if partName, ok := props["PARTNAME"]; ok {
if partName == "partition" {
return filepath.EvalSymlinks(fmt.Sprintf("/dev/block/%d:%d", props["MAJOR"], props["MINOR"]))
}
}
}
}

return "", FilesystemLabelNotFoundError{Label: label}
}


func (d *disk) MountPointIsFromDisk(mountpoint string, opts *Options) (bool, error) {
d2, err := diskFromMountPointImpl(mountpoint, opts)
if err != nil {
Expand Down
4 changes: 1 addition & 3 deletions secboot/secboot_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func UnlockVolumeUsingSealedKeyIfEncrypted(disk disks.Disk, name string, encrypt
// looking for the encrypted device to unlock, later on in the boot
// process we will look for the decrypted device to ensure it matches
// what we expected
partUUID, err := disk.FindMatchingPartitionUUID(name + "-enc")
encdev, err := disk.FindMatchingPartition(name + "-enc")
var errNotFound disks.FilesystemLabelNotFoundError
if xerrors.As(err, &errNotFound) {
// didn't find the encrypted label, so return nil to try the
Expand All @@ -249,8 +249,6 @@ func UnlockVolumeUsingSealedKeyIfEncrypted(disk disks.Disk, name string, encrypt
if err != nil {
return err, false
}
encdev := filepath.Join("/dev/disk/by-partuuid", partUUID)

mapperName = name + "-" + randutilRandomKernelUUID()
if !tpmDeviceAvailable {
return unlockEncryptedPartitionWithRecoveryKey(mapperName, encdev), true
Expand Down