Search code examples
clinuxhard-drivesysfs

Portable way to determine sector size in Linux


I want to write a small program in C which can determine the sector size of a hard disk. I wanted to read the file located in /sys/block/sd[X]/queue/hw_sector_size, and it worked in CentOS 6/7.

However when I tested in CentOS 5.11, the file hw_sector_size is missing, and I have only found max_hw_sectors_kb and max_sectors_kb.

Thus, I'd like to know how can I determine (APIs) the sector size in CentOS 5, or is there an other better way to do so. Thanks.


Solution

  • The fdisk utility displays this information (and runs successfully on kernels older even than than the 2.6.x vintage on CentOS 5), so that seems a likely place to look for an answer. Fortunately, we're living in the wonderful world of open source, so all it requires is a little investigation.

    The fdisk program is provided by the util-linux package, so we need that first.

    The sector size is displayed in the output of fdisk like this:

    Disk /dev/sda: 477 GiB, 512110190592 bytes, 1000215216 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    

    If we look for Sector size in the util-linux code, we find it in disk-utils/fdisk-list.c:

    fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"),
                fdisk_get_sector_size(cxt),
                fdisk_get_physector_size(cxt));
    

    So, it looks like we need to find fdisk_get_sector_size, which is defined in libfdisk/src/context.c:

    unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
    {
        assert(cxt);
        return cxt->sector_size;
    }
    

    Well, that wasn't super helpful. We need to find out where cxt->sector_size is set:

    $ grep -lri 'cxt->sector_size.*=' | grep -v tests
    libfdisk/src/alignment.c
    libfdisk/src/context.c
    libfdisk/src/dos.c
    libfdisk/src/gpt.c
    libfdisk/src/utils.c
    

    I'm going to start with alignment.c, since that filename sounds promising. Looking through that file for the same regex I used to list the files, we find this:

    cxt->sector_size = get_sector_size(cxt->dev_fd);
    

    Which leads me to:

    static unsigned long get_sector_size(int fd)
    {
        int sect_sz;
    
        if (!blkdev_get_sector_size(fd, &sect_sz))
            return (unsigned long) sect_sz;
        return DEFAULT_SECTOR_SIZE;
    }
    

    Which in turn leads me to the definition of blkdev_get_sector_size in lib/blkdev.c:

    #ifdef BLKSSZGET
    int blkdev_get_sector_size(int fd, int *sector_size)
    {
        if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
            return 0;
        return -1;
    }
    #else
    int blkdev_get_sector_size(int fd __attribute__((__unused__)), int *sector_size)
    {
        *sector_size = DEFAULT_SECTOR_SIZE;
        return 0;
    }
    #endif
    

    And there we go. There is a BLKSSZGET ioctl that seems useful. A search for BLKSSZGET leads us to this stackoverflow question, which includes the following information in a comment:

    For the record: BLKSSZGET = logical block size, BLKBSZGET = physical block size, BLKGETSIZE64 = device size in bytes, BLKGETSIZE = device size/512. At least if the comments in fs.h and my experiments can be trusted. – Edward Falk Jul 10 '12 at 19:33