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 incorrectdf
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)
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.
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.
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.
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.
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
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)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With