From 600f3740581f8542fcb31ce07ad186163481aaee Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 14 Mar 2023 17:21:52 +0100 Subject: [PATCH] btrfs-progs: qgroup show: fix formatting of qgroupid on json output On a 32bit host the split qgroupid is wrong due to the way the numbers are passed to the formatter as variable length arguments. The level is u16, promoted to int and then parsed as u64. This means that the values are shifted and some stack data are printed instead. Example error messages from yast2-bootloader: SystemCmd.cc(addLine):569 Adding Line 7 " "qgroupid": "21474836480/23885859321282560"," The value 21474836480 = 0x5000000 is 0x5 shifted by 32 bits, 23885859321282560 is 0x54dc1000000000 and shifting by 32 does not lead to a valid value which should be 0 in this case. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=1209136 Signed-off-by: David Sterba --- cmds/qgroup.c | 2 +- common/format-output.c | 8 ++++++-- tests/cli-tests/005-qgroup-show/test.sh | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cmds/qgroup.c b/cmds/qgroup.c index a2bbd945..49014d57 100644 --- a/cmds/qgroup.c +++ b/cmds/qgroup.c @@ -1475,7 +1475,7 @@ static void print_all_qgroups_json(struct qgroup_lookup *qgroup_lookup) fmt_print_start_group(&fctx, NULL, JSON_TYPE_MAP); fmt_print(&fctx, "qgroupid", - btrfs_qgroup_level(qgroup->qgroupid), + (int)btrfs_qgroup_level(qgroup->qgroupid), btrfs_qgroup_subvolid(qgroup->qgroupid)); fmt_print(&fctx, "referenced", qgroup->info.referenced); if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_RFER) diff --git a/common/format-output.c b/common/format-output.c index 18993ad6..67cc71be 100644 --- a/common/format-output.c +++ b/common/format-output.c @@ -347,10 +347,14 @@ void fmt_print(struct format_ctx *fctx, const char* key, ...) } else if (strcmp(row->fmt, "list") == 0) { } else if (strcmp(row->fmt, "map") == 0) { } else if (strcmp(row->fmt, "qgroupid") == 0) { - const u64 level = va_arg(args, u64); + /* + * Level is u16 but promoted to int when it's a vararg, callers + * should add explicit cast. + */ + const int level = va_arg(args, int); const u64 id = va_arg(args, u64); - printf("%llu/%llu", level, id); + printf("%hu/%llu", level, id); } else if (strcmp(row->fmt, "size-or-none") == 0) { const u64 size = va_arg(args, u64); const unsigned int unit_mode = va_arg(args, unsigned int); diff --git a/tests/cli-tests/005-qgroup-show/test.sh b/tests/cli-tests/005-qgroup-show/test.sh index c1454367..cc1b1b3a 100755 --- a/tests/cli-tests/005-qgroup-show/test.sh +++ b/tests/cli-tests/005-qgroup-show/test.sh @@ -13,9 +13,13 @@ prepare_test_dev run_check_mkfs_test_dev run_check_mount_test_dev run_mayfail "$TOP/btrfs" qgroup show "$TEST_MNT" +run_mayfail "$TOP/btrfs" --format json qgroup show "$TEST_MNT" run_mayfail $SUDO_HELPER "$TOP/btrfs" qgroup show "$TEST_MNT" +run_mayfail $SUDO_HELPER "$TOP/btrfs" --format json qgroup show "$TEST_MNT" run_check $SUDO_HELPER "$TOP/btrfs" quota enable "$TEST_MNT" run_mayfail "$TOP/btrfs" qgroup show "$TEST_MNT" +run_mayfail "$TOP/btrfs" --format json qgroup show "$TEST_MNT" run_check $SUDO_HELPER "$TOP/btrfs" qgroup show "$TEST_MNT" +run_check $SUDO_HELPER "$TOP/btrfs" --format json qgroup show "$TEST_MNT" run_check $SUDO_HELPER "$TOP/btrfs" quota disable "$TEST_MNT" run_check_umount_test_dev