Krishna
Krishna

Reputation: 1382

statvfs system call fails with error Value too large for defined data type

I have a Red Hat Enterprise Linux Server release 6.6 (2.6.32-504.el6.x86_64) installed on my server and have below partitions hierarchy.

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

I issue a statvfs call on each of the

/mediax

partition but the system call fails with error Value too large for defined data type.

I was able to find the system call returned error EOVERFLOW but not sure which member of struct statvfs resulted in this.

Does it have to do anything with the size of the /mediax partitions.

Note: Partitions are of xfs file system type.

Upvotes: 4

Views: 2970

Answers (2)

Nominal Animal
Nominal Animal

Reputation: 39406

As mentioned in the man 2 statfs man page:

The original Linux statfs() and fstatfs() system calls were not designed with extremely large file sizes in mind. Subsequently, Linux 2.6 added new statfs64() and fstatfs64() system calls that employ a new structure, statfs64. The new structure contains the same fields as the original statfs structure, but the sizes of various fields are increased, to accommodate large file sizes. The glibc statfs() and fstatfs() wrapper functions transparently deal with the kernel differences.

In your case, you are using the non-64-bit versions of the syscalls for some reason.

The Linux kernel implements the fstat*fs*() and stat*fs*() library calls using four different syscalls (plus optionally their compatibility versions): fstatfs(), fstatfs64(), statfs(), and statfs64(). All four are defined in fs/statfs.c in the kernel sources, and use kernel internal function vfs_statfs() to gather the necessary information into a struct kstatfs structure.

Both statfs() and fstatfs() use the kernel internal function do_statfs_native() (in fs/statfs.c) to copy the fields from a kernel struct kstatfs to the userspace struct statfs buffer. The problem is, many of the kernel structure fields are larger than the fields in the userspace buffer. The do_statfs_native() verifies that the values fit, and if not, will return -EOVERFLOW otherwise.

That is the only case I can find that may cause any of the four syscalls to return -EOVERFLOW.

For statfs64() and fstatfs64(), kernel function do_statfs64() is used to copy the fields from the kernel-internal struct kstatfs to an userspace struct statfs64 buffer. The userspace buffer fields are at least as large as the kernel structure fields are, so there is no risk of overflow. (The function never returns -EOVERFLOW.)

The fix is to ensure you use the 64-bit versions of the struct statfs, and the corresponding syscalls.


To ensure glibc uses the correct versions (the ones that can correctly describe very large filesystems) of the structures, ensure you have

#define _FILE_OFFSET_BITS 64

before any #includes; or, add -D_FILE_OFFSET_BITS=64 to your compiler flags.

All this does is it ensures that glibc knows that you are running on a Linux 2.6 or later kernel (3.x, 4.x, and so on), and that it definitely should try and use the versions of the structures with properly-sized fields.

Alternatively, you can define _LARGEFILE64_SOURCE, to expose the struct statfs64 and struct statvfs64 types and corresponding statfs64() and statvfs64() wrappers around the syscalls. This avoids letting glibc do any guesswork, and ensures you use the versions of the syscalls that can correctly describe all Linux filesystem sizes.

Both of these options work on all Linux architectures, 32-bit and 64-bit.

Upvotes: 3

Matthias
Matthias

Reputation: 8180

Not a full answer, but regarding the kernel code (v4.9, I have currently no code of your kernel, but I doubt that this part has changed a lot), you should search for a value > 0xffffffff for one of the following parameters: f_blocks, f_bfree, f_bavail, f_bsize, f_frsize, f_files or f_files, i.e., any parameter except f_fsid, f_flag, and f_namemax:

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;
}

Upvotes: 1

Related Questions