Skip to content

Commit

Permalink
Detect block device size using platform-specific IOCTL on Mac & Linux (
Browse files Browse the repository at this point in the history
…#173)

* build: Move os-specific diskfs dependencies to explicit modules

Darwin and Windows specific diskfs features already live in their own
module. Linux support however remains in a catch-all "unix" modules that
builds only for Linux in practice.

This commit makes the Unix module only target Linux and introduces a new
catch-all-other module to stub out block device specific functions.

This paves the road for using platform-specific IOCTL to get the
physical block device size and ultimately help with the Windows port of
block device support.

Signed-off-by: Jean-Tiare Le Bigot <jt@yadutaf.fr>

* diskfs: Move to platform-specific IOCTL to get block device size

Currently, diskfs attempts to detect the size of a block device using a
"seek" based approach. Unfortunately, while this looks generic, it does
not work on Mac, at least not in a VM.

Based on the previous commit, this commit adds a Mac and Linux
implementation for getting the size of a Block device through IOCTL
inspired by https://github.com/tytso/e2fsprogs/blob/master/lib/ext2fs/getsize.c

This was manually tested using a Linux loopback device and in a Mac
virtual machine.

Closes #171

Signed-off-by: Jean-Tiare Le Bigot <jt@yadutaf.fr>

---------

Signed-off-by: Jean-Tiare Le Bigot <jt@yadutaf.fr>
  • Loading branch information
yadutaf committed Mar 11, 2023
1 parent 3fb7269 commit a78c15f
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 16 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,35 @@ jobs:
run: echo ${{ github.event_name }}
build:
name: Build
strategy:
matrix:
target: [
# Tier 1
{arch: amd64, os: linux}, {arch: arm64, os: linux},
{arch: amd64, os: windows}, {arch: arm64, os: windows},
{arch: amd64, os: darwin}, {arch: arm64, os: darwin},

# Tier 2 (Best effort)
{arch: amd64, os: freebsd}, {arch: arm64, os: freebsd},
{arch: amd64, os: netbsd}, {arch: arm64, os: netbsd},
{arch: amd64, os: openbsd}, {arch: arm64, os: openbsd},
{arch: amd64, os: solaris},
{arch: amd64, os: illumos},
{arch: ppc64, os: aix},
]
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: ^1.16
- run: go build
env:
GOOS: ${{ matrix.target.os }}
GOARCH: ${{ matrix.target.arch }}
test:
name: Test
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
Expand Down
10 changes: 2 additions & 8 deletions diskfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ package diskfs
import (
"errors"
"fmt"
"io"
"os"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -213,14 +212,9 @@ func initDisk(f *os.File, openMode OpenModeOption, sectorSize SectorSize) (*disk
case mode&os.ModeDevice != 0:
log.Debug("initDisk(): block device")
diskType = disk.Device
file, err := os.Open(f.Name())
size, err = getBlockDeviceSize(f)
if err != nil {
return nil, fmt.Errorf("error opening block device %s: %s", f.Name(), err)
}
defer file.Close()
size, err = file.Seek(0, io.SeekEnd)
if err != nil {
return nil, fmt.Errorf("error seeking to end of block device %s: %s", f.Name(), err)
return nil, fmt.Errorf("error getting block device %s size: %s", f.Name(), err)
}
lblksize, pblksize, err = getSectorSizes(f)
log.Debugf("initDisk(): logical block size %d, physical block size %d", lblksize, pblksize)
Expand Down
21 changes: 16 additions & 5 deletions diskfs_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@ const (
DKIOCGETBLOCKCOUNT = 0x40086419
)

// getBlockDeviceSize get the size of an opened block device in Bytes.
func getBlockDeviceSize(f *os.File) (int64, error) {
fd := f.Fd()

blockSize, err := unix.IoctlGetInt(int(fd), DKIOCGETBLOCKSIZE)
if err != nil {
return 0, fmt.Errorf("unable to get device logical sector size: %v", err)
}

blockCount, err := unix.IoctlGetInt(int(fd), DKIOCGETBLOCKCOUNT)
if err != nil {
return 0, fmt.Errorf("unable to get device block count: %v", err)
}
return int64(blockSize) * int64(blockCount), nil
}

// getSectorSizes get the logical and physical sector sizes for a block device
func getSectorSizes(f *os.File) (logicalSectorSize, physicalSectorSize int64, err error) {
//nolint:gocritic // we keep this for reference to the underlying syscall
/*
ioctl(fd, BLKPBSZGET, &physicalsectsize);
*/
fd := f.Fd()

logicalSectorSizeInt, err := unix.IoctlGetInt(int(fd), DKIOCGETBLOCKSIZE)
Expand Down
12 changes: 9 additions & 3 deletions diskfs_unix.go → diskfs_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build linux || solaris || aix || freebsd || illumos || netbsd || openbsd || plan9
// +build linux solaris aix freebsd illumos netbsd openbsd plan9

package diskfs

import (
Expand All @@ -10,6 +7,15 @@ import (
"golang.org/x/sys/unix"
)

// getBlockDeviceSize get the size of an opened block device in Bytes.
func getBlockDeviceSize(f *os.File) (int64, error) {
blockDeviceSize, err := unix.IoctlGetInt(int(f.Fd()), unix.BLKGETSIZE64)
if err != nil {
return 0, fmt.Errorf("unable to get block device size: %v", err)
}
return int64(blockDeviceSize), nil
}

// getSectorSizes get the logical and physical sector sizes for a block device
func getSectorSizes(f *os.File) (logicalSectorSize, physicalSectorSize int64, err error) {
//
Expand Down
18 changes: 18 additions & 0 deletions diskfs_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//go:build !windows && !linux && !darwin

package diskfs

import (
"errors"
"os"
)

// getBlockDeviceSize get the size of an opened block device in Bytes.
func getBlockDeviceSize(f *os.File) (int64, error) {
return 0, errors.New("block devices not supported on this platform")
}

// getSectorSizes get the logical and physical sector sizes for a block device
func getSectorSizes(f *os.File) (logicalSectorSize, physicalSectorSize int64, err error) {
return 0, 0, errors.New("block devices not supported on this platform")
}
5 changes: 5 additions & 0 deletions diskfs_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import (
"os"
)

// getBlockDeviceSize get the size of an opened block device in Bytes.
func getBlockDeviceSize(f *os.File) (int64, error) {
return 0, errors.New("block devices not supported on windows")
}

// getSectorSizes get the logical and physical sector sizes for a block device
func getSectorSizes(f *os.File) (int64, int64, error) {
return 0, 0, errors.New("block devices not supported on windows")
Expand Down

0 comments on commit a78c15f

Please sign in to comment.