Skip to content

snap-bootstrap: add scan-disk subcommand - #12006

Merged
alfonsosanchezbeato merged 8 commits into
canonical:masterfrom
valentindavid:valentindavid/udev-scan-disk
May 7, 2025
Merged

snap-bootstrap: add scan-disk subcommand#12006
alfonsosanchezbeato merged 8 commits into
canonical:masterfrom
valentindavid:valentindavid/udev-scan-disk

Conversation

@valentindavid

@valentindavid valentindavid commented Jul 28, 2022

Copy link
Copy Markdown
Member

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
Copy Markdown
Member Author

This works with canonical/core-initrd#107

@mardy mardy left a comment

Copy link
Copy Markdown
Contributor

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?

Comment thread cmd/snap-bootstrap/cmd_scan_disk.go Outdated

Copy link
Copy Markdown
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
Copy Markdown
Member 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
Copy Markdown
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
Copy Markdown
Member 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
Copy Markdown
Member 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
Copy Markdown
Member 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
Copy Markdown
Member 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
Copy Markdown
Member 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
Copy Markdown
Contributor

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
Copy Markdown
Member 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
Copy Markdown
Member 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

xnox commented Aug 15, 2022

Copy link
Copy Markdown
Contributor

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
Copy Markdown
Member 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
Copy Markdown
Contributor

@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
Copy Markdown
Member 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
Copy Markdown
Member 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

pedronis commented Apr 11, 2025 via email

Copy link
Copy Markdown
Contributor

@alfonsosanchezbeato

Copy link
Copy Markdown
Contributor

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
Copy Markdown
Contributor

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

valentindavid commented Apr 22, 2025

Copy link
Copy Markdown
Member Author
  • 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
Copy Markdown
Contributor
  • 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
Copy Markdown
Member 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.

@andrewphelpsj andrewphelpsj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

@@ -1810,24 +1810,46 @@ func getNonUEFISystemDisk(fallbacklabel string) (string, error) {
// 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
Copy Markdown
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.

Comment thread cmd/snap-bootstrap/cmd_scan_disk.go Outdated
}

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

Copy link
Copy Markdown
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.

Comment thread cmd/snap-bootstrap/cmd_scan_disk.go Outdated
* this is not an error. There are plenty of block devices
* that are not the boot device.
*/
return nil;

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
Copy Markdown
Member 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
Copy Markdown
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?

Comment thread cmd/snap-bootstrap/blkid/blkid.go Outdated
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
Copy Markdown
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
Copy Markdown
Member 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
Copy Markdown
Member Author

I have resplit and rebased all the commits for merge.

@pedronis pedronis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thanks, couple minor things

Comment thread cmd/snap-bootstrap/cmd_scan_disk.go Outdated

Copy link
Copy Markdown
Contributor

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

Comment thread cmd/snap-bootstrap/cmd_scan_disk.go Outdated

Copy link
Copy Markdown
Contributor

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
Copy Markdown
Member 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.

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