Steins;Lab

  • 项目
  • 折腾
  • 笔记
  • 图册
  • 杂谈
  • 文章索引 - 博主自荐博文
  • 关于/留言
Steins;Lab
某团的自留研究所
  1. 首页
  2. 折腾
  3. 正文

活久见!记一次 fdisk 磁盘大小显示 bug

2022年9月17日 3060点热度 0人点赞 2条评论

在日常操作中,惊奇地发现老牌磁盘工具 util-linux fdisk 的一个容量显示 bug。笔者根据该值配置线上环境,最终导致了磁盘越界问题,好在最终影响范围不大。另外,也追踪源码,探究一下 fdisk 中的磁盘容量的计算方式。

TL;DR

是 fdisk 转换成 TiB 显示时的 bug。 该问题在 2.36 版本解决。写这篇文章时,ubuntu 20 的 apt 仓库仍在 2.34。问题仍然存在。

应对方案:若遇到容量显示不一致,优先使用 byte 原始值。

1 问题产生

使用 fdisk -l 查看所有磁盘的容量,得到上述结果。版本为 fdisk from util-linux 2.34。

1.9TiB 和 1199638052864bytes 明显不对应。

笔者在一开始注意到了上述的差异,但是凭借对老牌工具的信赖,没有深入怀疑。以为可能是某些 raid 设置或者冗余容量导致的差异。

2 源码追踪

问询了几位老司机后无果,自己快速追一下代码。

util-linux 中字节格式转换全部由函数 size_to_human_string 负责。

有问题的代码在 19 年 2 月由 commit 07b94c9f 引入,旨在支持保留2位小数的功能:support two decimal places in size_to_human_string() output。

问题代码位于 lib/strutils.c 如下 (release-2.33):

char *size_to_human_string(int options, uint64_t bytes)
{
    char buf[32];
    int dec, exp;
    uint64_t frac;
    const char *letters = "BKMGTPE";
    char suffix[sizeof(" KiB")], *psuf = suffix;
    char c;

    if (options & SIZE_SUFFIX_SPACE)
        *psuf++ = ' ';

    // 求字节等级和余数
    exp  = get_exp(bytes);
    c    = *(letters + (exp ? exp / 10 : 0));
    dec  = exp ? bytes / (1ULL << exp) : bytes;
    frac = exp ? bytes % (1ULL << exp) : 0;

    *psuf++ = c;

    if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
        *psuf++ = 'i';
        *psuf++ = 'B';
    }

    *psuf = '\0';

    /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
     *                 exp, suffix[0], dec, frac);
     */

    /* round */
    if (frac) {
        if (options & SIZE_DECIMAL_2DIGITS) {
            // !!! 问题代码
            frac = (frac / (1ULL << (exp - 10)) + 5) / 10;
            if (frac % 10 == 0)
                frac /= 10; /* convert N.90 to N.9 */
        } else {
            frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
            if (frac == 10)
                dec++, frac = 0;
        }
    }

    if (frac) {
        struct lconv const *l = localeconv();
        char *dp = l ? l->decimal_point : NULL;

        if (!dp || !*dp)
            dp = ".";
        snprintf(buf, sizeof(buf), "%d%s%" PRIu64 "%s", dec, dp, frac, suffix);
    } else
        snprintf(buf, sizeof(buf), "%d%s", dec, suffix);

    return strdup(buf);
}

其中, 先求得容量等级 exp 用于展示 KB/MB/GB...,后求得下一级的字节余数 frac,用于渲染小数点后字符串。

问题出在 SIZE_DECIMAL_2DIGITS 时,如果 frac 在 10~95 范围内就会翻车。

    if (frac) {
        if (options & SIZE_DECIMAL_2DIGITS) {
            // 笔者添加了一个中间变量用于调试
            auto frac_temp = frac / (1ULL << (exp - 10)) + 5;
            // 如果余数 frac=90,得到的小数点后字符串为 "9",期望值为 "09"
            frac = (frac / (1ULL << (exp - 10)) + 5) / 10;
            if (frac % 10 == 0)
                frac /= 10; /* convert N.90 to N.9 */

该问题后续于 2020 年在 2.36 版本修复。参考:issue#998。

3 Extra: fdisk 容量的计算方式

fdisk 使用 libfdisk 获取磁盘底层信息。

磁盘的总 byte 数由 sector 数量 * sector 大小得来。

    uint64_t bytes = fdisk_get_nsectors(cxt) * fdisk_get_sector_size(cxt);
    char *strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
                       | SIZE_SUFFIX_SPACE
                       | SIZE_SUFFIX_3LETTER, bytes);

    color_scheme_enable("header", UL_COLOR_BOLD);
    fdisk_info(cxt, _("Disk %s: %s, %ju bytes, %ju sectors"),
            fdisk_get_devname(cxt), strsz,
            bytes, (uintmax_t) fdisk_get_nsectors(cxt));

4 小结

  1. fdisk 转换成 TiB 显示时的 bug。 该问题在 2.36 版本解决。写这篇文章时,ubuntu 20 的 apt 仓库仍在 2.34。问题仍然存在。
  2. fdisk -l 中的 human 可读容量和后面的 byte 容量来源一致。
  3. fdisk 使用 libfdisk 获取磁盘底层信息。磁盘的总 byte 数由 sector 数量 * sector 大小得来。

另外,老司机的代码也可能翻车,时隔1年2个月才修复。作为开发者的我们如果能多写一些单元测试,此类问题就可能提前暴露避免。

相关

标签: fdisk
最后更新:2022年9月24日

SPtuan

SPtuan 是一名普通的工程师,最大的愿望是度过平静的时光。 当前从事网络/CDN/对象存储研发。

点赞
< 上一篇
下一篇 >
5 1 vote
文章评分
Subscribe
Login
提醒
guest

guest

2 评论
最新
最旧 得票最多
Inline Feedbacks
View all comments
wangtc
wangtc
2 年 之前

最近虾皮大规模毕业,站长有受到影响吗?

0
回复
SPtuan
SPtuan
作者
Reply to  wangtc
2 年 之前

@wangtc 暂时还没被影响到。不过资本的本质是无情的,俺永远做好准备

0
回复

SPtuan

SPtuan 是一名普通的工程师,最大的愿望是度过平静的时光。
当前从事网络/CDN/对象存储研发。

  • TL;DR
  • 1 问题产生
  • 2 源码追踪
  • 3 Extra: fdisk 容量的计算方式
  • 4 小结
分类
  • Uncategorized
  • 图册
  • 学习笔记
  • 库
  • 折腾
  • 杂谈
  • 瞎**扯
  • 碎碎念
  • 项目跟踪
最近评论
SPtuan 发布于 2 个月前(03月22日) 书签: 关于 disk-io 的经验, 异步/同步 io 系统设计的经验 https://you...
SPtuan 发布于 2 个月前(03月21日) 如果公司不是对外提供这些服务的,这种岗位都是 infra 部门,平均年龄确实会大一些。尤其构建和维护...
HUA 发布于 2 个月前(03月19日) 想请问博主对于国内CDN行业,以及CDN调度、DNS托管类服务相关岗位的看法,以及是否还推荐校招新人...
SPtuan 发布于 3 个月前(02月03日) 2025 注: 长辈对于只身去深圳的担忧,更多地来自于 80s/90s 治安情况。近几年了解了严打...
SPtuan 发布于 4 个月前(01月16日) 哈哈,100就100吧,新年快乐!
热门主题 & 页面
  • [实验]VPS搭建ss服务中转实现纯ipv6访问网络-校园网免流量
  • HTTP Header Name 大小写敏感问题
  • 使用 WSL2 + X11 转发 - 在 Windows10 中打造 GNU/Linux 学习生产环境
  • iowait 到底是什么?
  • 基于openvpn的校园网ipv6免流量方案 |ipv6|校园网|免流量
归档
  • 2025 年 3 月
  • 2024 年 12 月
  • 2024 年 9 月
  • 2024 年 8 月
  • 2024 年 5 月
  • 2024 年 3 月
  • 2024 年 2 月
  • 2023 年 12 月
  • 2023 年 11 月
  • 2023 年 9 月
  • 2023 年 8 月
  • 2023 年 4 月
  • 2023 年 1 月
  • 2022 年 12 月
  • 2022 年 10 月
  • 2022 年 9 月
  • 2022 年 7 月
  • 2022 年 6 月
  • 2022 年 2 月
  • 2021 年 12 月
  • 2021 年 11 月
  • 2021 年 2 月
  • 2021 年 1 月
  • 2020 年 9 月
  • 2020 年 4 月
  • 2020 年 3 月
  • 2020 年 1 月
  • 2019 年 8 月
  • 2019 年 7 月
  • 2019 年 5 月
  • 2019 年 4 月
  • 2019 年 3 月
  • 2019 年 2 月
  • 2018 年 12 月
  • 2018 年 10 月
  • 2018 年 9 月
  • 2018 年 8 月
  • 2018 年 5 月
  • 2018 年 2 月
  • 2018 年 1 月
  • 2017 年 11 月
  • 2017 年 9 月
  • 2017 年 7 月
  • 2017 年 6 月
  • 2017 年 5 月
  • 2017 年 4 月
  • 2017 年 3 月
  • 2017 年 2 月
  • 2017 年 1 月
  • 2016 年 12 月
  • 2016 年 11 月
  • 2016 年 10 月
  • 2016 年 9 月
  • 2016 年 8 月
  • 2016 年 7 月
  • 2016 年 6 月
  • 2016 年 5 月
  • 2016 年 4 月
  • 2016 年 3 月
  • 2016 年 2 月
  • 2016 年 1 月
  • 2015 年 12 月
  • 2015 年 11 月
  • 2015 年 9 月

友情链接:

Blessing Studio hahaschool 绘枫和畅 魔法少女Fandy monsterx Clarke的博客 Luminous’ Home Shintaku's Blog
蓝黑的博客 haruhi.club Yida的博客 Bo2SS 涛叔 TangBao 同和君Hocassian

Steins;Lab 团子神社 zdfmc.net

steinslab.io built with ❤. Thanks for all 2015-2025.

Theme Kratos Made By Seaton Jiang

wpDiscuz