Merge lp://qastaging/~mvo/snappy/maybe-move-syncbootfiles into lp://qastaging/~snappy-dev/snappy/snappy-moved-to-github

Proposed by Michael Vogt
Status: Work in progress
Proposed branch: lp://qastaging/~mvo/snappy/maybe-move-syncbootfiles
Merge into: lp://qastaging/~snappy-dev/snappy/snappy-moved-to-github
Prerequisite: lp://qastaging/~mvo/snappy/rename-update-bootloader
Diff against target: 110 lines (+12/-32)
3 files modified
partition/partition.go (+9/-16)
snappy/systemimage.go (+1/-9)
snappy/systemimage_test.go (+2/-7)
To merge this branch: bzr merge lp://qastaging/~mvo/snappy/maybe-move-syncbootfiles
Reviewer Review Type Date Requested Status
Sergio Schvezov Abstain
James Hunt (community) Disapprove
Review via email: mp+249832@code.qastaging.launchpad.net

Description of the change

This branch moves bootloader.SyncBootFiles out of snappy/systemimage.go
and into partition/partition.go.

It seems like its always called before ToggleRootFS and forgetting it seems potentially dangerous so it seems its best that the partition code deal with it directly instead of leaving this to the caller (which may easily forget it and forces the caller to have knowledge that he/she may not have).

Feedback welcome!

To post a comment you must log in.
Revision history for this message
Sergio Schvezov (sergiusens) wrote :

I'm +1 on this, if it always needs to be done and nothing intermediate can be done with the resulting call, it should be internal. Thanks

review: Approve
Revision history for this message
James Hunt (jamesodhunt) wrote :

This is wrong I'm afraid: bootloader.SyncBootFiles() must be called *before* DownloadUpdate() - see the comment above the call to s.partition.SyncBootloaderFiles() in systemimage.go:Install().

Essentially, the code needs to copy the existing kernel+initrd+dtb files when preparing to switch rootfs. This must happen before s-i is involved to guarantee that there actually is a kernel+initrd to boot the "other" rootfs from (since although both partitions are provisioned on a u-boot system, only /boot/uboot/a/ is populated. And if "snappy update" does _not_ install newer kernel/initrd/dtb files, the system wouldn't boot.

If we do the copy afterwards (as this branch does), we will silently switch back to the old kernel+initrd+dtb.

review: Disapprove
Revision history for this message
James Hunt (jamesodhunt) wrote :

FTR, I'm not against making the calls internal, but the implementation here would break u-boot systems.

Revision history for this message
Sergio Schvezov (sergiusens) wrote :

Given jodh's comment, that should be set as a code comment and the reordering as well.

review: Abstain
Revision history for this message
Michael Vogt (mvo) wrote :

Thanks for the review! Great that this was found before it could do harm.

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks again, I'm having trouble understanding the issue here right now, could you help me?

The way I read the code in toggleBootloaderRootfs() (which is called indirectly from systemimage.go via ToggleNextBoot) is this:

1 stuff is downloaded via proxy.DownloadUpdate() and the ubuntu-core-upgrader runs
2 SyncBootFiles is called and it copies /boot/uboot/a -> /boot/uboot/b
3 ToggleRootFS() is called and that switches to "b"
4 HandleAssets is called which looks at /writable/cache/hardware.yaml and may override /boot/uboot/b/ again.

In the current code (1) and (2) are switched, what is happening in (1) that breaks (2).

My understanding was that HandleAssets() in (4) would copy the downloaded stuff from (1) into /boot/uboot/b/ (if there is anything). So I guess my disconnect is that I don't quite understand what is happening in (1), i.e. whats the role of s-i (or maybe ubuntu-core-upgrader) that breaks when SyncBootFiles() is not called before s-i is run?

Revision history for this message
James Hunt (jamesodhunt) wrote :

s.partition.SyncBootloaderFiles() must happen before s.proxy.DownloadUpdate() because there is no guarantee that an s-i update will include a new kernel+initrd combo.

The current code does effectively:

1) cp /boot/uboot/a/* to /boot/uboot/b/.
2) download, and unpack new s-i.
3) toggle bootloader config to boot from 'b'.

So, if (2) does *NOT* include a kernel+initrd, the system will still boot 'b' using the original kernel+initrd. If we remove (1), the system is unbootable in that scenario. But if (2) does include a new kernel+initrd, great - that gets installed by HandleAssets().

But imagine we changed the logic to:

1) download, and unpack new s-i.
2) cp /boot/uboot/a/* to /boot/uboot/b/.
3) toggle bootloader config to boot from 'b'.

If the new s-i in (1) did contain a new kernel+initrd, you've just overwritten them with the old ('a' partition) versions.

Revision history for this message
Michael Vogt (mvo) wrote :

Aha, my understanding was that the copy to /boot/uboot/b happens in handleAssets which is called after syncbootloader files. Is that where my disconnect is? I. E. Ubuntu-core-upgrader is what copies kernel,initrd to /boot/uboot/b?

On 18 February 2015 18:41:32 CET, James Hunt <email address hidden> wrote:
>s.partition.SyncBootloaderFiles() must happen before
>s.proxy.DownloadUpdate() because there is no guarantee that an s-i
>update will include a new kernel+initrd combo.
>
>The current code does effectively:
>
>1) cp /boot/uboot/a/* to /boot/uboot/b/.
>2) download, and unpack new s-i.
>3) toggle bootloader config to boot from 'b'.
>
>So, if (2) does *NOT* include a kernel+initrd, the system will still
>boot 'b' using the original kernel+initrd. If we remove (1), the system
>is unbootable in that scenario. But if (2) does include a new
>kernel+initrd, great - that gets installed by HandleAssets().
>
>But imagine we changed the logic to:
>
>1) download, and unpack new s-i.
>2) cp /boot/uboot/a/* to /boot/uboot/b/.
>3) toggle bootloader config to boot from 'b'.
>
>If the new s-i in (1) did contain a new kernel+initrd, you've just
>overwritten them with the old ('a' partition) versions.
>
>
>
>
>
>
>
>--
>https://code.launchpad.net/~mvo/snappy/maybe-move-syncbootfiles/+merge/249832
>You are the owner of lp:~mvo/snappy/maybe-move-syncbootfiles.

--
Sent from my Android phone with K-9 Mail. Please excuse my brevity.

Revision history for this message
James Hunt (jamesodhunt) wrote :

HandleAssets copies any newer kernel/initrd/dtb files after the update *if* the new s-i provides them. So, there are potentially 2 copies but the first one has to happen before the upgrade in case there is nothing for HandleAssets to do.

Revision history for this message
Michael Vogt (mvo) wrote :

Right, the SyncBootFiles() call is still there and it will copy /boot/uboot/a -> /boot/uboot/b so if there is no new kernel the one from /boot/uboot/a will be used. Its just partition-internal now and the order is slightly different, i.e. its run after the download, but AIUI the download itself will not modify /boot/uboot/{a,b} so that would be ok? Then there is handleAssets which is also run and may override the /boot/uboot/b kernel with a newer version, that has not changed.

Revision history for this message
James Hunt (jamesodhunt) wrote :

After a verbal chat, the confusion has now been cleared... :-)

I've just reviewed this code a little more carefully, and it is in fact correct. Although the SyncBootFiles() happens after the upgrader has run, that is actually safe since the upgrader doesn't modify the boot partition (currently). Hence, we can legitimately call:

1) upgrader.
2) SyncBootFiles().
3) HandleAssets().

I still feel that from a defensive programming perspective though, it is safer to handle the sync before the upgrader runs (at the expense of a single export), just in case the behaviour of the upgrader changes (say if we ever support in-place upgrades). However, given sufficiently good tests [1], such a change should be catchable to ensure such a bug doesn't escape into the wild.

[1] - that checksum the assets on the boot partition and asserts that those relating to the "current" rootfs haven't changed post-upgrade.

Unmerged revisions

179. By Michael Vogt

make SyncBootloaderFiles() part of toggleBootloaderRootfs()

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
The diff is not available at this time. You can reload the page or download it.

Subscribers

People subscribed via source and target branches