When --incremental=data is run on a merged image, erofs_write_device_table()
leaves unified addresses and total block count stale after the primary device
grows. Fix it by rewriting the slots in place with updated uniaddr value.

Reproducer:

mkfs.erofs -Enoinline_data a.erofs a/
mkfs.erofs -Enoinline_data b.erofs b/
mkfs.erofs merged.erofs a.erofs b.erofs
mkfs.erofs --incremental=data merged.erofs c/

Signed-off-by: Jonathan Calmels <[email protected]>
---
 include/erofs/internal.h |  6 ++----
 lib/super.c              | 27 +++++++++++++++------------
 2 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 95f5627..b10e447 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -123,10 +123,8 @@ struct erofs_sb_info {
        u32 checksum;
        u16 available_compr_algs;
        u16 extra_devices;
-       union {
-               u16 devt_slotoff;               /* used for mkfs */
-               u16 device_id_mask;             /* used for others */
-       };
+       u16 devt_slotoff;               /* used for mkfs */
+       u16 device_id_mask;             /* used for others */
        erofs_nid_t packed_nid;
        erofs_nid_t metabox_nid;
 
diff --git a/lib/super.c b/lib/super.c
index 9e52dd8..672464c 100644
--- a/lib/super.c
+++ b/lib/super.c
@@ -52,6 +52,7 @@ static int erofs_init_devices(struct erofs_sb_info *sbi,
 
        sbi->extra_devices = ondisk_extradevs;
        sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1;
+       sbi->devt_slotoff = le16_to_cpu(dsb->devt_slotoff);
        sbi->devs = calloc(ondisk_extradevs, sizeof(*sbi->devs));
        if (!sbi->devs)
                return -ENOMEM;
@@ -424,15 +425,15 @@ int erofs_write_device_table(struct erofs_sb_info *sbi)
        if (!sbi->extra_devices)
                goto out;
        if (!bh) {
-               if (erofs_sb_has_device_table(sbi))
-                       return 0;
-               return -EINVAL;
-       }
-
-       pos = erofs_btell(bh, false);
-       if (pos == EROFS_NULL_ADDR) {
-               DBG_BUGON(1);
-               return -EINVAL;
+               if (!erofs_sb_has_device_table(sbi))
+                       return -EINVAL;
+               pos = sbi->devt_slotoff * EROFS_DEVT_SLOT_SIZE;
+       } else {
+               pos = erofs_btell(bh, false);
+               if (pos == EROFS_NULL_ADDR) {
+                       DBG_BUGON(1);
+                       return -EINVAL;
+               }
        }
 
        i = 0;
@@ -452,9 +453,11 @@ int erofs_write_device_table(struct erofs_sb_info *sbi)
                nblocks += sbi->devs[i].blocks;
        } while (++i < sbi->extra_devices);
 
-       bh->op = &erofs_drop_directly_bhops;
-       erofs_bdrop(bh, false);
-       sbi->bh_devt = NULL;
+       if (bh) {
+               bh->op = &erofs_drop_directly_bhops;
+               erofs_bdrop(bh, false);
+               sbi->bh_devt = NULL;
+       }
 out:
        sbi->total_blocks = nblocks;
        return 0;
-- 
2.53.0


Reply via email to