diff --git a/osspecifics/osspecifics.go b/osspecifics/osspecifics.go index 8551c40..44a4ec4 100644 --- a/osspecifics/osspecifics.go +++ b/osspecifics/osspecifics.go @@ -89,3 +89,23 @@ func CheckRunAsRoot() (bool, error) { return currentUser.Username == "root", nil } + +func GetDeviceLogicalBlockSize(devPath string) (uint64, error) { + fd, err := os.Open(devPath) + if err != nil { + return 0, errors.Wrap(err, "open device") + } + + defer func() { _ = fd.Close() }() + + bs, err := getDeviceLogicalBlockSizeInner(fd.Fd()) + if err != nil { + return 0, errors.Wrap(err, "get block size inner") + } + + if bs <= 0 { + return 0, fmt.Errorf("retrieved block size is zero (or negative): '%v'", bs) + } + + return uint64(bs), nil +} diff --git a/osspecifics/osspecifics_cross.go b/osspecifics/osspecifics_cross.go deleted file mode 100644 index d2cc31f..0000000 --- a/osspecifics/osspecifics_cross.go +++ /dev/null @@ -1,44 +0,0 @@ -// Linsk - A utility to access Linux-native file systems on non-Linux operating systems. -// Copyright (c) 2023 The Linsk Authors. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package osspecifics - -import ( - "fmt" - "os" - - "github.com/pkg/errors" -) - -func GetDeviceLogicalBlockSize(devPath string) (uint64, error) { - fd, err := os.Open(devPath) - if err != nil { - return 0, errors.Wrap(err, "open device") - } - - defer func() { _ = fd.Close() }() - - bs, err := getDeviceLogicalBlockSizeInner(fd.Fd()) - if err != nil { - return 0, errors.Wrap(err, "get block size inner") - } - - if bs <= 0 { - return 0, fmt.Errorf("retrieved block size is zero (or negative): '%v'", bs) - } - - return uint64(bs), nil -} diff --git a/osspecifics/osspecifics_windows.go b/osspecifics/osspecifics_windows.go index d6c6d55..767b235 100644 --- a/osspecifics/osspecifics_windows.go +++ b/osspecifics/osspecifics_windows.go @@ -19,6 +19,7 @@ package osspecifics import ( + "encoding/binary" "fmt" "os/exec" "regexp" @@ -99,3 +100,31 @@ func CheckRunAsRoot() (bool, error) { return member, nil } + +func GetDeviceLogicalBlockSize(devPath string) (uint64, error) { + diskPath, err := windows.UTF16PtrFromString(devPath) + if err != nil { + return 0, errors.Wrap(err, "create utf-16 ptr from dev path string") + } + + handle, err := windows.CreateFile(diskPath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0) + if err != nil { + return 0, errors.Wrap(err, "create windows file") + } + + defer func() { _ = windows.CloseHandle(handle) }() + + buf := make([]uint8, 128) + var read uint32 + err = windows.DeviceIoControl(handle, 0x700a0, nil, 0, &buf[0], uint32(len(buf)), &read, nil) // IOCTL_DISK_GET_DRIVE_GEOMETRY_EX call. + if err != nil { + return 0, errors.Wrap(err, "invoke windows device i/o control") + } + + // Skipping cylinders, media type, tracks per cylinder, and sectors per track fields in the disk geometry return struct. + // + // We could theoretically use `unsafe` type casting, but it's in the name - it's unsafe. + bs := binary.NativeEndian.Uint32(buf[20:24]) + + return uint64(bs), nil +}