Repository navigation
mtd: an alias is a partition others cover, not one that holds another - #265
Conversation
mtd_mark_aliases() dropped any partition that contained another whole. That is right for Novatek's "all", and wrong for a layout that nests partitions without covering the parent: with firmware 0x50000-0x800000 split into kernel and rootfs, and rootfs_data as the tail of rootfs, it dropped firmware and also rootfs for holding rootfs_data. Since #235, on any kernel that reports offsets, that took 0x250000-0x6d0000 out of everything enumerated. `backup` and `upload --backup` left out the squashfs without a word: 0x380296 bytes for an 8 MB flash in an emulated run of that layout. `restore --raw` refused with "no partition covers 0x250000-0x6d0000", which is how this was found, on a hi3516cv6xx camera. A partition is now an alias only if smaller ones inside it reach from its start to its end. firmware goes, rootfs stays. raw_image_plan() takes the larger of two partitions starting together, so a parent that remains is written through rather than taken for an overlap with its child. Run on an emulated hi3516ev300 with the layout above, placing partitions from sysfs, the command line and the boot log: restore --raw leaves the flash equal to the image, and the backup comes to 0x9302d7 bytes. rootfs_data is in that backup twice, and plain `restore` refuses it with "Offset algorithm error" and the flash unchanged, as it did before.
PR Summary by QodoPreserve nested MTD partitions unless their children fully cover them
AI Description
Diagram
High-Level Assessment
Files changed (5)
|
Code Review by Qodo
1.
|
Partitions can nest now that a partially covered parent is kept, so the sum stopped being the flash. The report said 9M for an 8 MB chip, as Qodo found on this PR; it had said 3M before, with the parent dropped. The sum is also where upgrade takes the end of the flash from, which was worse than the report. With rootfs_data inside rootfs and no firmware parent, the settings region ran 0x130000 past the end, those offsets mapped into rootfs_data, which is the tail of the flash, and they erased what had just been written there. In an emulated run of a full nor8m bundle, 8 blocks of the new rootfs at 0x6d0000-0x750000 came out as 0xff, with the upgrade reporting success. Master refused that layout, so this was new in the previous commit. enum_mtd_info() now works out the union of the ranges it keeps, for the report, restore and upgrade alike. A partition with no known offset can only be added on, as before. The same run writes the rootfs intact.
mtd_mark_aliases()dropped any partition that contained another whole. That is right for Novatek'sall, but wrong when partitions nest without covering their parent.What went wrong
Reported on a hi3516cv6xx camera with 8 MB of NOR:
firmwareiskernel+rootfs, androotfs_datais the tail ofrootfs. The old rule droppedfirmware, which is correct, and alsorootfsfor holdingrootfs_data, which leaves0x250000-0x6d0000unreachable.restore --rawrefused withno partition covers 0x250000-0x6d0000.Backups are affected, and this dates from #235. On any kernel that reports offsets,
backupandupload --backupgo through the same filter, so on this layout they silently left out the squashfs. Existing backups from such cameras are likely incomplete. Whether a camera is affected can be read from/proc/mtd: a partition that holds another but is not fully covered by the partitions inside it.The fix
A partition is an alias only if smaller partitions inside it reach from its start to its end.
firmwaregoes,rootfsstays.raw_image_plan()takes the larger of two partitions starting at the same offset, so a parent that remains is written through instead of being taken for an overlap with its child.The flash size is now the union of the partitions' ranges rather than the sum of their sizes, for the report,
restoreandupgrade. A partition with no known offset can only be added on, as before.allon Novatek is still dropped, since its partitions cover it. Anallwith an unpartitioned hole in it is now kept rather than dropped, so a backup holds that flash twice instead of losing the hole.Why the flash size had to change
Keeping a nested parent makes the sum larger than the chip. Beyond the report saying
9Mfor 8 MB,upgradetakes the end of the flash from that sum. Withrootfs_datainsiderootfsand nofirmwareparent, the settings region ran past the end, wrapped intorootfs_data, and erased the tail of the rootfs just written, while reporting success. This came from this PR's first commit, not from master.Testing
Run on an emulated hi3516ev300 (kernel 4.9, 8 MB NOR) with the layout above, comparing the flash file from the host afterwards:
restore --raw, offsets from sysfsrestore --raw, offsets from the command linerestore --raw, offsets from the boot logbackupsize0x3802960x9302d7upgrade --wipe-settingswith a fullnor8mbundle, 7 MB of images:firmwareparentrootfs_datainrootfs, no parentThe report for the first layout says
3Mon master, and8Mnow.The 21 scenarios from #264 also pass. The native test binaries pass, and restoring the old rule fails
mtd_alias_test.Not tested: real hardware, and the bytes of the new backup: it was checked by size only.
Still open
On this layout
rootfs_datais in the backup twice, and plainrestorerefuses the backup withOffset algorithm error, leaving the flash unchanged. It did the same before this change. So these backups hold everything, butrestorecannot put them back yet. That needs its own fix: placing blocks by real offset, as--rawdoes.upgradestill refuses the layout with afirmwareparent, without writing, becausemap_old_new_mtd()stops at the slot the dropped parent leaves. On the way it logsKeeping (0 KB)for that slot and prints bootargs with an empty partition and a shiftedroot=, which it never writes. The same fix would cover this.