logical_disk: Get Volume ID for NTFS Volume Mounts (#1752)

Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2024-11-21 20:59:11 +01:00
committed by GitHub
parent 85845f4780
commit 3b378136f5

View File

@@ -59,6 +59,7 @@ type Collector struct {
} }
type volumeInfo struct { type volumeInfo struct {
diskIDs string
filesystem string filesystem string
serialNumber string serialNumber string
label string label string
@@ -288,9 +289,8 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
// to the provided prometheus Metric channel. // to the provided prometheus Metric channel.
func (c *Collector) Collect(ch chan<- prometheus.Metric) error { func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
var ( var (
err error err error
diskID string info volumeInfo
info volumeInfo
) )
perfData, err := c.perfDataCollector.Collect() perfData, err := c.perfDataCollector.Collect()
@@ -299,19 +299,10 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
} }
for name, volume := range perfData { for name, volume := range perfData {
if name == "_Total" || if c.config.VolumeExclude.MatchString(name) || !c.config.VolumeInclude.MatchString(name) {
c.config.VolumeExclude.MatchString(name) ||
!c.config.VolumeInclude.MatchString(name) {
continue continue
} }
diskID, err = getDiskIDByVolume(name)
if err != nil {
c.logger.Warn("failed to get disk ID for "+name,
slog.Any("err", err),
)
}
info, err = getVolumeInfo(name) info, err = getVolumeInfo(name)
if err != nil { if err != nil {
c.logger.Warn("failed to get volume information for "+name, c.logger.Warn("failed to get volume information for "+name,
@@ -323,7 +314,7 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
c.information, c.information,
prometheus.GaugeValue, prometheus.GaugeValue,
1, 1,
diskID, info.diskIDs,
info.volumeType, info.volumeType,
name, name,
info.label, info.label,
@@ -472,38 +463,60 @@ func getDriveType(driveType uint32) string {
const diskExtentSize = 24 const diskExtentSize = 24
// getDiskIDByVolume returns the disk ID for a given volume. // getDiskIDByVolume returns the disk ID for a given volume.
func getDiskIDByVolume(rootDrive string) (string, error) { func getVolumeInfo(rootDrive string) (volumeInfo, error) {
// Open a volume handle to the Disk Root. volumePath := rootDrive
var err error
var f windows.Handle // If rootDrive is a NTFS directory, convert it to a volume GUID.
if strings.Contains(volumePath, `\`) {
volumePathName, err := windows.UTF16PtrFromString(volumePath + `\`)
if err != nil {
return volumeInfo{}, fmt.Errorf("could not convert rootDrive to volume path: %w", err)
}
volumeGUIDPtr := make([]uint16, 50)
if err := windows.GetVolumeNameForVolumeMountPoint(volumePathName, &volumeGUIDPtr[0], uint32(len(volumeGUIDPtr))); err != nil {
panic(fmt.Errorf("could not get volume GUID: %w", err))
}
volumePath = windows.UTF16ToString(volumeGUIDPtr)
// GetVolumeNameForVolumeMountPoint returns the volume GUID path as \\?\Volume{GUID}\
// According https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol#remarks
// Win32 Drive Namespace is prefixed with \\.\, so we need to remove the \\?\ prefix.
volumePath, _ = strings.CutPrefix(volumePath, `\\?\`)
// https://stackoverflow.com/questions/55710326/how-to-get-the-physical-device-that-a-volume-guid-path-belongs-to#comment98104360_55710326
// DeviceIoControl expects no trailing backslash in the volume GUID path.
volumePath = strings.TrimRight(volumePath, `\`)
}
volumePathPtr := windows.StringToUTF16Ptr(`\\.\` + volumePath)
// mode has to include FILE_SHARE permission to allow concurrent access to the disk. // mode has to include FILE_SHARE permission to allow concurrent access to the disk.
// use 0 as access mode to avoid admin permission. // use 0 as access mode to avoid admin permission.
mode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE) mode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE)
attr := uint32(windows.FILE_ATTRIBUTE_READONLY)
f, err = windows.CreateFile( volumeHandle, err := windows.CreateFile(volumePathPtr, 0, mode, nil, windows.OPEN_EXISTING, attr, 0)
windows.StringToUTF16Ptr(`\\.\`+rootDrive),
0, mode, nil, windows.OPEN_EXISTING, uint32(windows.FILE_ATTRIBUTE_READONLY), 0)
if err != nil { if err != nil {
return "", err return volumeInfo{}, fmt.Errorf("could not open volume for %s: %w", rootDrive, err)
} }
defer windows.Close(f) defer windows.Close(volumeHandle)
controlCode := uint32(5636096) // IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS controlCode := uint32(5636096) // IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
volumeDiskExtents := make([]byte, 16*1024) volumeDiskExtents := make([]byte, 16*1024)
var bytesReturned uint32 var bytesReturned uint32
err = windows.DeviceIoControl(f, controlCode, nil, 0, &volumeDiskExtents[0], uint32(len(volumeDiskExtents)), &bytesReturned, nil) err = windows.DeviceIoControl(volumeHandle, controlCode, nil, 0, &volumeDiskExtents[0], uint32(len(volumeDiskExtents)), &bytesReturned, nil)
if err != nil { if err != nil {
return "", fmt.Errorf("could not identify physical drive for %s: %w", rootDrive, err) return volumeInfo{}, fmt.Errorf("could not identify physical drive for %s: %w", rootDrive, err)
} }
numDiskIDs := uint(binary.LittleEndian.Uint32(volumeDiskExtents)) numDiskIDs := uint(binary.LittleEndian.Uint32(volumeDiskExtents))
if numDiskIDs < 1 { if numDiskIDs < 1 {
return "", fmt.Errorf("could not identify physical drive for %s: no disk IDs returned", rootDrive) return volumeInfo{}, fmt.Errorf("could not identify physical drive for %s: no disk IDs returned", rootDrive)
} }
diskIDs := make([]string, numDiskIDs) diskIDs := make([]string, numDiskIDs)
@@ -515,34 +528,35 @@ func getDiskIDByVolume(rootDrive string) (string, error) {
slices.Sort(diskIDs) slices.Sort(diskIDs)
diskIDs = slices.Compact(diskIDs) diskIDs = slices.Compact(diskIDs)
return strings.Join(diskIDs, ";"), nil volumeInformationRootDrive := volumePath + `\`
}
func getVolumeInfo(rootDrive string) (volumeInfo, error) { if strings.Contains(volumePath, `Volume`) {
if !strings.HasSuffix(rootDrive, ":") { volumeInformationRootDrive = `\\?\` + volumeInformationRootDrive
return volumeInfo{}, nil
} }
volPath := windows.StringToUTF16Ptr(rootDrive + `\`) volumeInformationRootDrivePtr := windows.StringToUTF16Ptr(volumeInformationRootDrive)
driveType := windows.GetDriveType(volumeInformationRootDrivePtr)
volBufLabel := make([]uint16, windows.MAX_PATH+1) volBufLabel := make([]uint16, windows.MAX_PATH+1)
volSerialNum := uint32(0) volSerialNum := uint32(0)
fsFlags := uint32(0) fsFlags := uint32(0)
volBufType := make([]uint16, windows.MAX_PATH+1) volBufType := make([]uint16, windows.MAX_PATH+1)
driveType := windows.GetDriveType(volPath) err = windows.GetVolumeInformation(
volumeInformationRootDrivePtr,
err := windows.GetVolumeInformation(volPath, &volBufLabel[0], uint32(len(volBufLabel)), &volBufLabel[0], uint32(len(volBufLabel)),
&volSerialNum, nil, &fsFlags, &volBufType[0], uint32(len(volBufType))) &volSerialNum, nil, &fsFlags,
&volBufType[0], uint32(len(volBufType)),
)
if err != nil { if err != nil {
if driveType != windows.DRIVE_CDROM && driveType != windows.DRIVE_REMOVABLE { if driveType == windows.DRIVE_CDROM || driveType == windows.DRIVE_REMOVABLE {
return volumeInfo{}, err return volumeInfo{}, nil
} }
return volumeInfo{}, nil return volumeInfo{}, fmt.Errorf("could not get volume information for %s: %w", volumeInformationRootDrive, err)
} }
return volumeInfo{ return volumeInfo{
diskIDs: strings.Join(diskIDs, ";"),
volumeType: getDriveType(driveType), volumeType: getDriveType(driveType),
label: windows.UTF16PtrToString(&volBufLabel[0]), label: windows.UTF16PtrToString(&volBufLabel[0]),
filesystem: windows.UTF16PtrToString(&volBufType[0]), filesystem: windows.UTF16PtrToString(&volBufType[0]),