Skip to content

Conversation

@valentindavid
Copy link
Contributor

@valentindavid valentindavid commented Jul 28, 2022

This command is expected to be called from udev. This creates
symlinks /dev/ubuntu/ubuntu-{seed,boot,data,save}.

Then we can bind snap-bootstrap initramfs-mounts to
dev-ubuntu-ubuntu-seed.device.

@valentindavid
Copy link
Contributor Author

This works with canonical/core-initrd#107

Copy link
Contributor

@mardy mardy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to get the same information just by running blkid instead of using the library?

} else if part.Name == "ubuntu-data-enc" {
has_data = true
data_uuid = part.UUID
} else if part.Name == "ubuntu-data" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if someone appends unencrypted ubuntu-data to a hard-drive, then it will be mounted instead of ubuntu-data-enc? surely we should try to find -enc paritions, and not search for non-encrypted ones afterwords?

Also I thought udev database already has all of this information inside it's database, which maybe can be queried directly in the udev rule alone without this helper binary? Apart from the LoaderDevicePartUUID bit, which i thought there should be a udev symlink for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if someone appends unencrypted ubuntu-data to a hard-drive, then it will be mounted instead of ubuntu-data-enc? surely we should try to find -enc paritions, and not search for non-encrypted ones afterwords?

Maybe I mixed up, but I think the filesystem labels have "-enc" suffix. But the partition name does not. See for example:

/dev/vda4: UUID="2eeba654-032f-4384-8a46-075be2b415b5" LABEL="ubuntu-save-enc" TYPE="crypto_LUKS" PARTLABEL="ubuntu-save" PARTUUID="a154643d-4398-714a-9704-71335e5ea856"

PARTLABEL is not the same as LABEL

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside ubuntu-save-enc there is ubuntu-save. Outside of ubuntu-save-enc there can be a malicious second partition with an FS ubuntu-save. Does above code handle this fine? As far as I can tell, after scanning and finding ubuntu-data-enc and using it; it will then find unencrypted ubuntu-data and override it.

Somehow this feels odd that after the first one is detected, it can be detected again, and override previously found result.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I thought udev database already has all of this information inside it's database, which maybe can be queried directly in the udev rule alone without this helper binary? Apart from the LoaderDevicePartUUID bit, which i thought there should be a udev symlink for.

Good question. I have tried before to use more rules and less code. But there were issues. And I do not remember. I will try again to document why this is not possible.

I am guessing it was the fallback for non-uefi boot that was a bit tricky.

gpt-auto-generator can create a symlink. But it has to be a partition with a specific uuid on the same disk as the booting partition. We do not use that uuid. And we should not use it. It is not made for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside ubuntu-save-enc there is ubuntu-save. Outside of ubuntu-save-enc there can be a malicious second partition with an FS ubuntu-save.

I am not sure I understood this.

This binary only looks at partition names. Not filesystem labels. If there are other partitions with conflicting filesystem labels, we will not look at them, because we only look at the partition names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today, someone can remove an encrypted ubuntu-save and replace it by a non encrypted one. snap-bootstrap will then fallback to it. What would be the difference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I thought udev database already has all of this information inside it's database, which maybe can be queried directly in the udev rule alone without this helper binary? Apart from the LoaderDevicePartUUID bit, which i thought there should be a udev symlink for.

Good question. I have tried before to use more rules and less code. But there were issues. And I do not remember. I will try again to document why this is not possible.

Now I remember. It is possible to not call it on ENV{DEVTYPE}=="partition". Though we need to make sure that IMPORT{builtin}="blkid [...]" has been called yet, so post 60-persistent-storage.rules. Then what we have to do is compare ID_PART_ENTRY_UUID with UBUNTU_SEED (or the 3 others).

However, for ENV{DEVTYPE}=="disk" we do need to scan the partitions and find the booting one. And this we cannot do with just udev rules. We have LoaderDevicePartUUID which points to the boot partition. But we are processing the disk at this point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpt-auto-generator can create a symlink.

By the way, I was wrong about it. It is in an udev rule that the symlink is created. gpt-auto-generator only makes sysroot.mount and -.mount to use that symlink.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I remember. It is possible to not call it on ENV{DEVTYPE}=="partition". Though we need to make sure that IMPORT{builtin}="blkid [...]" has been called yet, so post 60-persistent-storage.rules. Then what we have to do is compare ID_PART_ENTRY_UUID with UBUNTU_SEED (or the 3 others).

what's the conclusion here? it seemed related to what Alfonso is also asking below. Anyway as Alfonso said this needs a bit of a description of what is the logic/flow and why we went that way in the code itself

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking back at this thread, I realize that the -enc suffix is only for the luks2 label. Not the partition label. So maybe I can use only udev rules for the partitions.

@valentindavid
Copy link
Contributor Author

Would it be possible to get the same information just by running blkid instead of using the library?

We cannot list partitions from blkid.

@xnox
Copy link
Contributor

xnox commented Aug 15, 2022

Given SEED is ESP; and we bind the initramfs-mounts unit to the SEED; i am wondering if we can instead turn on / patch the systemd's gpt-auto-generator which can create esp mount in /efi and use that instead?

Looking more at this, I actually don't quite like the behaviour of gpt-auto-generator. Because the good bits of it are not what we need here.

Ideally we would only read efi_loader_get_device_part_uuid (aka LoaderDevicePartUUID) and generate .device unit for it, and bind initramfs-mounts service unit to it. Before any drives appear.

Thus without waiting udev to exec scan-disk to process them all either.

@valentindavid
Copy link
Contributor Author

Ideally we would only read efi_loader_get_device_part_uuid (aka LoaderDevicePartUUID) and generate .device unit for it, and bind initramfs-mounts service unit to it. Before any drives appear.

Thus without waiting udev to exec scan-disk to process them all either.

So almost what I have done, but a symlink on the whole disk rather than the partitions?

@pedronis pedronis added the Needs Samuele review Needs a review from Samuele before it can land label Aug 22, 2022
@pedronis pedronis changed the title snap-bootstrap: Add scan-disk subcommand snap-bootstrap: add scan-disk subcommand Aug 23, 2022
@pedronis pedronis self-requested a review September 19, 2022 13:19
@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch 2 times, most recently from 4e7a21e to 999e91c Compare December 13, 2022 10:42
@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch 2 times, most recently from 49c9437 to 4060872 Compare February 14, 2023 12:09
@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch 3 times, most recently from ebb8a9b to 7b6adbd Compare February 22, 2023 10:06
@pedronis
Copy link
Collaborator

@valentindavid can the description or some comment in the code include the actual udev rules that this will be used from? it's a bit unclear just from the code how things go from the code in scan-disk to have /dev/ubuntu/disk symlinked?

What's the plan for testing the cmd_scan_disk code?

@valentindavid
Copy link
Contributor Author

@valentindavid can the description or some comment in the code include the actual udev rules that this will be used from? it's a bit unclear just from the code how things go from the code in scan-disk to have /dev/ubuntu/disk symlinked?

Sure. We could even add the rules to snapd and install them into core-initrd.

What's the plan for testing the cmd_scan_disk code?

I think I will need to mock libblkid somehow to test it.

Loop devices need root to be setup, and I do not think there is as this point any nice user land block devices. I think only character devices can be emulated with CUSE. Not block.

@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch from 7b6adbd to 71cc643 Compare March 3, 2023 14:51
@valentindavid
Copy link
Contributor Author

What's the plan for testing the cmd_scan_disk code?

I have added tests.

I still have to add tests for the changes in cmd_initramfs_mounts.go (when /dev/ubuntu/disk exists). But that should be straight forward.

@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch 4 times, most recently from 2f663fa to 05f7751 Compare March 9, 2023 12:37
@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch from 42a76f8 to 8b70e59 Compare March 10, 2023 10:21
@valentindavid valentindavid marked this pull request as ready for review March 23, 2023 15:19
@pedronis
Copy link
Collaborator

pedronis commented Apr 11, 2025 via email

@alfonsosanchezbeato
Copy link
Member

af69054 has a possible solution to the described race (#15359). It makes the scan command export an additional variable with the number of expected partitions and a specific symlink is created with all those partitions have appeared.

@alfonsosanchezbeato
Copy link
Member

af69054 has a possible solution to the described race (#15359). It makes the scan command export an additional variable with the number of expected partitions and a specific symlink is created with all those partitions have appeared.

I've changed a bit the approach and use now UUIDs, see 2328620

@valentindavid
Copy link
Contributor Author

valentindavid commented Apr 22, 2025

  • There should not be any uevent emitted until devtmpfs has been populated, that means once /dev/disks/ubuntu/disk can only appear after the device nodes have appeared in /dev. See https://github.com/torvalds/linux/blob/a33b5a08cbbdd7aadff95f40cbb45ab86841679e/block/genhd.c#L463
  • We do not follow yet other symlinks in this PR because we would need to add dependency to the unit to them. But this is a bit complicated. It might depend on splitting snap-boostrap execution into several units.

@alfonsosanchezbeato
Copy link
Member

  • There is should not any uevent emitted until devtmpfs has been populated, that means once /dev/disks/ubuntu/disk can only appear after the device nodes have appeared in /dev. See https://github.com/torvalds/linux/blob/a33b5a08cbbdd7aadff95f40cbb45ab86841679e/block/genhd.c#L463
  • We do not follow yet other symlinks in this PR because we would need to add dependency to the unit to them. But this is a bit complicated and done before. It might depend on splitting snap-boostrap execution into several units.

Thanks for checking! Re: waiting on symlinks I think it should be possible if the scan commands provides information to udev on what symlink/partition we want to wait for depending on the mode. And create a symlink on which mount command can wait that could point to different partitions.

@valentindavid
Copy link
Contributor Author

Re: waiting on symlinks I think it should be possible if the scan commands provides information to udev on what symlink/partition we want to wait for depending on the mode. And create a symlink on which mount command can wait that could point to different partitions.

The problem here is that we need to add dependencies in generators. And that would require daemon-reload. And start commands as well. The best way is to split snap-bootstrap so we can add correct dependencies to every part. This branch works fine as is as long as we do not expect to read the symlinks yet...

But they will be useful as soon as we move things out. For instance we could move mount of boot and seed as automount units.

Copy link
Member

@andrewphelpsj andrewphelpsj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

// determine what partition the booted kernel came from. If which disk the
// kernel came from cannot be determined, then it will fallback to mounting via
// the specified disk label.
func mountNonDataPartitionMatchingKernelDisk(dir, fallbacklabel string, opts *systemdMountOptions) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc comment here I think could use an update to explain this new flow.

}

partitions, err := probe.GetPartitions()
if partitions == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a normal check on err != nil would be more consistent here.

* this is not an error. There are plenty of block devices
* that are not the boot device.
*/
return nil;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably need to run gofmt on these files.

cnode := C.CString(node)
defer C.free(unsafe.Pointer(cnode))
C.blkid_new_probe_from_filename(cnode)
probe, err := C.blkid_new_probe_from_filename(cnode)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, err will contain some representation of errno, from C. Would it be safer to check both err and probe?

Do we know for sure err != nil when probe == nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Although that is a bit different, I think. I wanted to make sure that errno is always set by these functions on failure, but that isn't mentioned in the docs. I took a look at the code, and I think that errno would be set in all failure paths, but it is hard to tell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed it to use err.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use err, then we're saying that errno will always be set by the C function on failure. Is that definitely true?

func newProbeFromFilenameImpl(node string) (AbstractBlkidProbe, error) {
cnode := C.CString(node)
defer C.free(unsafe.Pointer(cnode))
C.blkid_new_probe_from_filename(cnode)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errant/extra call here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep... that was a leak.

@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch from 513059a to afe58c2 Compare April 30, 2025 15:11
@valentindavid
Copy link
Contributor Author

I have resplit and rebased all the commits for merge.

Copy link
Collaborator

@pedronis pedronis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, couple minor things

* is not an error. There are lots of block
* devices.
*/
return nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests don't get here

return scanDiskNodeFallback(output, node)
}

// TODO: split the rest of this function in 2, fallback and non fallback.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was taken care afaict

@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch 2 times, most recently from 8bf84c5 to f01a2ca Compare May 5, 2025 13:12
@valentindavid valentindavid force-pushed the valentindavid/udev-scan-disk branch from f01a2ca to 247246b Compare May 6, 2025 15:22
@valentindavid
Copy link
Contributor Author

Could I get a forced merge?

Failures:

Preparing:

* openstack:centos-9-64

This PR is not related to centos.

* google-nested:ubuntu-20.04-64:tests/nested/manual/core20-new-snapd-does-not-break-old-initrd

This looks like nvidia component related. Not related to this.

* google:ubuntu-22.04-64:tests/main/nvidia-files:550

Download error from package repositories

Executing:

* google-nested:ubuntu-24.04-64:tests/nested/manual/remodel-min-size

Serial port suggests it boot correctly. Only ssh fails to connect. That is after mounting disks.

Restoring:

* openstack:centos-9-64

This PR is not related to centos.

Also unit-tests-cross-distros, unrelated and already fixed in master.

@alfonsosanchezbeato alfonsosanchezbeato merged commit 54cfe92 into canonical:master May 7, 2025
75 of 83 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Samuele review Needs a review from Samuele before it can land Run nested The PR also runs tests inluded in nested suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants