Skip to content
Advertisement

Why is stat::st_size 0 for devices but at the same time lseek defines the device size correctly?

I noticed that when I query the size of a device using open + lseek, everything is OK, but when I stat the device, I get zero instead of the real device size. The device is clean without any file system and the first bytes of device start with some text like “1234567890ABC”. What is wrong?

The code:

#include <sys/stat.h>
#include <dirent.h>

bool
GetFileSize(const char* pPath, uint64_t& Size)
{
    pPath = "/home/sw/.bashrc";
    pPath = "/dev/sda";

    struct stat buffer;
    if (stat(pPath, &buffer))
    {
        printf("Failed to stat file. Error: %s. FilePath: %sn", strerror(errno), pPath);
        return false;
    }

    printf("File size by stat: %" PRIu64 " WTF?n", buffer.st_size);

    //
    // Note: It's strange, but stat::st_size from the stat call is zero for devices
    //

    int File = open(pPath, O_RDONLY);
    if (File < 0)
    {
        printf("Failed to open file. Error: %s. FilePath: %sn", strerror(errno), pPath);
        return false;
    }

    long off = lseek(File, 0, SEEK_END);
    if (off == (off_t)-1)
    {
        printf("Failed to get file size. Error: %s. FilePath: %sn", strerror(errno), pPath);
        close(File);
        return false;
    }
    close(File);

    printf("File size by lseek: %" PRIu64 "n", off);
    fflush(stdout);

    Size = off;
    return true;
}

Output:

File size by stat: 0 Huh?
File size by lseek: 34359738368

If I use stat for a regular file then everything is OK (comment out the line with “/dev/sda”):

File size by stat: 4019 Huh?
File size by lseek: 4019

Advertisement

Answer

The devil is in the detail… For starters, there is the fundamental principle of Unix design: everything is a file, Nicely explained here.

The second is that the stat(2) call is giving you inode statistics stored on the filesystem about the device-special file which has a size of zero (think of it as lstat(2)). If you have a block-device that has a filesystem on it you get information about it using statfs(2) or getfsstat(2) or statvfs(2) in a filesystem/device independent way.

Dealing with special files (usually residing in /dev) has always been system specific and the manual pages reside in section 4. So if you want to manipulate a device directly you should read up on the specifics there. For instance, in Linux man 4 hd will show you how to programmatically interact with IDE block devices. Whereas man 4 sd will give you how to interact with scsi discs, etc.

Third thing, system calls are not supposed to be inconsistent in their functionality NOR their limitations.

Hope this has helped.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement