Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a block device's size correctly in go?

Tags:

go

EDITED: as @abhink pointed out, was not invoking Size().

I tried two different go methods, and then compared to df. Of course, all 3 give different results:

package main

import (
    "os"
    "syscall"
    "fmt"
)

func main() {
    disk := "/dev/sda1"
    statout, err := os.Stat(disk)
    if err != nil {
        fmt.Errorf("Error %x", err)
        os.Exit(1)
    }
    println("os.Stat Size   : ", statout.Size())

    var stat syscall.Statfs_t
    syscall.Statfs(disk, &stat)
    println("syscall.Statfs_t Type: ",  stat.Type)
    println("syscall.Statfs_t Bsize: ",  stat.Bsize)
    println("syscall.Statfs_t Blocks: ",  stat.Blocks)
}

Running the programs:

$ main
os.Stat Size   :  0
syscall.Statfs_t Type: 16914836
syscall.Statfs_t Bsize: 4096
syscall.Statfs_t Blocks: 2560

And df:

$ df /dev/sda1
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda1             65792556  43694068  18726712  70% /var

Net:

  • os.Stat() gives 0 which it is not, but might be an OS issue.
  • syscall.Statfs() gives 2560 blocks * 4096 block size = 10,485,760. More realistic, but still incorrect
  • df gives 65792556 1K-blocks * 1024 bytes / K = 67,371,577,344

How do I reliably get the size of a block device without mounting it?

Essentially, I am looking for the equivalent of the ioctl call on the device.

ioctl(fd,BLKGETSIZE64,&size)
like image 618
deitch Avatar asked Oct 04 '17 07:10

deitch


People also ask

How do I find out the size of a block device?

blockdev --getsize64 /dev/sda returns size in bytes. blockdev --getsz /dev/sda returns size in 512-byte sectors. Deprecated: blockdev --getsize /dev/sda returns size in sectors. blockdev is part of util-linux.

What is block size in DD?

The dd command reports on the number of blocks it reads and writes. The number after the + is a count of the partial blocks that were copied. The default block size is 512 bytes.

What is block size in filesystem?

In a file system, a block is the largest contiguous amount of disk space that can be allocated to a file and also the largest amount of data that can be transferred in a single I/O operation. The block size determines the maximum size of a read request or write request that a file system sends to the I/O device driver.

How do I find the block size of filesystem in Linux?

The block size for any existing ext2 or ext3 filesystem (which are the most common filesystem types on Linux) can be obtained by using the dumpe2fs command with the device name as an argument (i.e., input data). The device name is the partition or disk on which the filesystem resides.


2 Answers

When calling syscall.Statfs(), you have to pass the path where the device is mounted, e.g. /, and not the device file (not /dev/sda1). In your case this is /var.

You get the result in a value of type syscall.Statfs_t. Interpretation:

var stat syscall.Statfs_t
if err := syscall.Statfs("/", &stat); err != nil {
    panic(err)
}

size := stat.Blocks * uint64(stat.Bsize)
free := stat.Bfree * uint64(stat.Bsize)
avail := stat.Bavail * uint64(stat.Bsize)
fmt.Println("Size:", size)
fmt.Println("Free:", free)
fmt.Println("Available:", avail)
fmt.Println("Used:", size-free)

See this possible duplicate: Get amount of free disk space using Go

like image 191
icza Avatar answered Oct 03 '22 20:10

icza


The OP asked how to get the size of a block device. To get the size of a block device (or any file), you can File.Seek to the end of the file using io.SeekEnd and read the position returned. Credit to others for python and C.

Running the example getsize.go below shows:

$ sudo go run getsize.go /dev/sda
/dev/sda is 256060514304 bytes.

lsblk --bytes /dev/device will give you the same information. That is how much data the block device can store.

The Statfs_t path, and df /path/to/mounted/filesystem will give you information about how much data you can store in the filesystem mounted at provided path. Filesystems have overhead, probably in the 2-5% range depending on details of the filesystem, and also keep track of how much space is Free or Used.

There is no api that I am aware of that can provide information about unmounted filesystems on a block device. dumpe2fs can give you that information for the ext{2,3,4} filesystems. There likely exist tools for other filesystems. Such tools are filesystem specific. When you mount the filesystem, then the linux kernel's filesystem driver exposes that information that is returned by df.

Code:

// getsize.go: get the size of a block device or file
package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    var path string
    if len(os.Args) < 2 {
        fmt.Println("Give path to file/disk")
        os.Exit(1)
    }
    path = os.Args[1]
    file, err := os.Open(path)
    if err != nil {
        fmt.Printf("error opening %s: %s\n", path, err)
        os.Exit(1)
    }
    pos, err := file.Seek(0, io.SeekEnd)
    if err != nil {
        fmt.Printf("error seeking to end of %s: %s\n", path, err)
        os.Exit(1)
    }
    fmt.Printf("%s is %d bytes.\n", path, pos)
}
like image 24
smoser Avatar answered Oct 03 '22 20:10

smoser