【问题标题】:statvfs system call fails with error Value too large for defined data typestatvfs 系统调用失败并出现错误 Value too large for defined data type
【发布时间】:2017-04-17 04:57:52
【问题描述】:

我的服务器上安装了 Red Hat Enterprise Linux Server 版本 6.6 (2.6.32-504.el6.x86_64),并且具有以下分区层次结构。

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       7.9G  1.7G  5.9G  22% /
tmpfs           5.4G  8.0K  5.4G   1% /dev/shm
/dev/sda8        53G  1.4G   49G   3% /mysql/data
/dev/sda6       7.9G  4.5G  3.1G  60% /usr/BWhttpd
/dev/sda4        32G  989M   29G   4% /var
/dev/sdb1        25T   37M   25T   1% /media1
/dev/sdc1        25T   37M   25T   1% /media2
/dev/sdd1        25T   37M   25T   1% /media3
/dev/sde1        22T   21T  1.1T  95% /media4

我对每个

发出一个statvfs 调用

/mediax

分区但系统调用失败并出现错误Value too large for defined data type

我能够找到系统调用返回错误EOVERFLOW,但不确定struct statvfs 的哪个成员导致了此错误。

它是否与 /mediax 分区的大小有关。

注意:分区是 xfs 文件系统类型。

【问题讨论】:

  • 您发布的输出看起来来自dfwhich uses statvfs() internally,因此statvfs() 似乎适用于您的系统。你是如何编译你的代码的?您是编译为 32 位还是 64 位?
  • @AndrewHenle 是的,输出是df 命令。我不确定 32/64 位编译是否会在这里有所作为。可以详细说明一下。
  • @Krishna_Oza:我跟踪了内核调用链(尽管只针对 ext4)。此外,您完全误解了问题的性质:本机字长,或者硬件是 32 位还是 64 位,与这里的问题无关。问题是用于struct statfsstruct statvfs 的原始结构具有无法描述为Linux 支持的大分区的字段。如果其中一个字段溢出,系统调用将返回EOVERFLOW所有架构上的所有 linux 内核 -- 32 64 位 -- 从 2.6 开始支持 statfs64()
  • @Krishna_Oza:我在水平线之后重写了答案中的部分以使其更清晰。重复一遍:这不是 32 位/64 位架构问题;只是原始结构使用的字段太小而无法保存实际的真实值。由于已有代码在使用它,内核无法更改它;它只能添加一个新的替换系统调用(用于新代码),该系统调用使用具有适当大小字段的结构。这发生在十多年前,两者仍然受到支持。在您的情况下,glibc 只是猜错了应该使用哪个,需要一些指导。
  • @Krishna_Oza:很高兴听到!我花了几次尝试以可理解的格式描述这些接口......还有许多其他与文件/文件系统相关的系统调用遇到类似问题 - 原始字段太小,或者可能没有足够的字段 - - 像lseek64();但是#define _FILE_OFFSET_BITS 64 宏应该足以告诉 glibc 使用具有普通函数 (lseek()) 和类型 (off_t, struct stat) 的 "better" 版本。跨度>

标签: c linux filesystems system-calls glibc


【解决方案1】:

man 2 statfs 手册页所述:

最初的 Linux statfs() 和 fstatfs() 系统调用在设计时并未考虑到极大的文件大小。随后,Linux 2.6 添加了采用新结构 statfs64 的新 statfs64() 和 fstatfs64() 系统调用。新结构包含与原始 statfs 结构相同的字段,但增加了各个字段的大小,以适应较大的文件大小。 glibc statfs() 和 fstatfs() 包装函数透明地处理内核差异。

在您的情况下,出于某种原因,您正在使用非 64 位版本的系统调用。

Linux 内核使用四个不同的系统调用(以及可选的它们的兼容版本)实现 fstat*fs*()stat*fs*() 库调用:fstatfs()fstatfs64()statfs()statfs64()。这四个都在内核源代码中的fs/statfs.c 中定义,并使用内核内部函数vfs_statfs() 将必要的信息收集到struct kstatfs 结构中。

statfs()fstatfs() 都使用内核内部函数 do_statfs_native()(在 fs/statfs.c 中)将字段从内核 struct kstatfs 复制到用户空间 struct statfs 缓冲区。问题是,许多内核结构字段大于用户空间缓冲区中的字段。 do_statfs_native() 验证值是否合适,如果不合适,将返回 -EOVERFLOW 否则。

这是我能找到的唯一可能导致四个系统调用中的任何一个返回 -EOVERFLOW 的情况。

对于statfs64()fstatfs64(),内核函数do_statfs64() 用于将字段从内核内部struct kstatfs 复制到用户空间struct statfs64 缓冲区。用户空间缓冲区字段至少与内核结构字段一样大,因此不存在溢出风险。 (该函数永远不会返回-EOVERFLOW。)

解决方法是确保您使用 64 位版本的 struct statfs 和相应的系统调用。


为确保 glibc 使用结构的正确版本(可以正确描述非常大的文件系统的版本),请确保您拥有

#define _FILE_OFFSET_BITS 64

在任何#includes 之前;或者,将-D_FILE_OFFSET_BITS=64 添加到您的编译器标志中。

所有这一切都是为了确保 glibc 知道您在 Linux 2.6 或更高版本的内核(3.x、4.x 等)上运行,并且它肯定应该尝试使用这些结构的版本具有适当大小的字段。

或者,您可以定义_LARGEFILE64_SOURCE,以公开struct statfs64struct statvfs64 类型以及相应的statfs64()statvfs64() 围绕系统调用的包装器。这避免了让 glibc 做任何猜测,并确保您使用可以正确描述所有 Linux 文件系统大小的系统调用版本。

这两个选项都适用于所有 Linux 架构,32 位和 64 位。

【讨论】:

  • 这是否以任何方式暗示我们可以挂载任何大尺寸的分区,或者分区仍然存在更高的阈值。
  • @Krishna_Oza:不,这并不意味着;只是如果可以挂载一个分区,我们就可以完全按照内核看到的那样获得它的统计信息。至于分区大小限制:磁盘上使用的分区表格式可以限制分区的大小(这可能会让一些人感到惊讶,并且是为大型存储驱动器迁移到 GPT 格式分区表的原因)。此外,每个文件系统都有其大小的限制。对于 ext4,我认为是 2^60 字节(1.15e18 字节)。理论上,通用文件系统代码最多可以容纳 2^60 个字节。当然,未经测试!
【解决方案2】:

不是一个完整的答案,但是关于内核代码(v4.9,我目前没有你的内核的代码,但我怀疑这部分已经改变了很多),你应该搜索一个值> 0xffffffff for以下参数之一:f_blocksf_bfreef_bavailf_bsizef_frsizef_filesf_files,即除f_fsid、@9876543315和@9876之外的任何参数@:

if (sizeof buf->f_blocks == 4) {
    if ((st.f_blocks | st.f_bfree | st.f_bavail |
         st.f_bsize | st.f_frsize) &
        0xffffffff00000000ULL)
        return -EOVERFLOW;
    /*
     * f_files and f_ffree may be -1; it's okay to stuff
     * that into 32 bits
     */
    if (st.f_files != -1 &&
        (st.f_files & 0xffffffff00000000ULL))
        return -EOVERFLOW;
    if (st.f_ffree != -1 &&
        (st.f_ffree & 0xffffffff00000000ULL))
        return -EOVERFLOW;
}

【讨论】:

  • 您这里指的是哪个内核文件,我可以看看与我的内核版本相同的文件。但是对于 18T 驱动器来说一切正常,而对于 22T 驱动器则失败了。
  • @Krishna_Oza: fs/statfs.c.
猜你喜欢
  • 1970-01-01
  • 2020-04-07
  • 2011-04-17
  • 2019-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-09
  • 1970-01-01
相关资源
最近更新 更多