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 <dsterba@suse.com>
This commit is contained in:
David Sterba 2023-03-14 17:21:52 +01:00
parent cba1ef1a42
commit 600f374058
3 changed files with 11 additions and 3 deletions

View file

@ -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)

View file

@ -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);

View file

@ -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