2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-09-28 07:59:27 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
// +build windows
|
|
|
|
|
|
|
|
package filestore
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
"syscall"
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
)
|
|
|
|
|
|
|
|
var errSharingViolation = syscall.Errno(32)
|
|
|
|
|
|
|
|
func isBusy(err error) bool {
|
|
|
|
err = underlyingError(err)
|
|
|
|
return err == errSharingViolation
|
|
|
|
}
|
|
|
|
|
|
|
|
func diskInfoFromPath(path string) (info DiskInfo, err error) {
|
|
|
|
absPath, err := filepath.Abs(path)
|
|
|
|
if err != nil {
|
|
|
|
absPath = path
|
|
|
|
}
|
|
|
|
var filesystemID string
|
|
|
|
var availableSpace int64
|
|
|
|
|
|
|
|
availableSpace, err = getDiskFreeSpace(absPath)
|
|
|
|
if err != nil {
|
|
|
|
return DiskInfo{"", -1}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
filesystemID, err = getVolumeSerialNumber(absPath)
|
|
|
|
if err != nil {
|
|
|
|
return DiskInfo{"", availableSpace}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return DiskInfo{filesystemID, availableSpace}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
kernel32 = syscall.MustLoadDLL("kernel32.dll")
|
|
|
|
procGetDiskFreeSpace = kernel32.MustFindProc("GetDiskFreeSpaceExW")
|
|
|
|
)
|
|
|
|
|
|
|
|
func getDiskFreeSpace(path string) (int64, error) {
|
|
|
|
path16, err := syscall.UTF16PtrFromString(path)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var bytes int64
|
|
|
|
_, _, err = procGetDiskFreeSpace.Call(uintptr(unsafe.Pointer(path16)), uintptr(unsafe.Pointer(&bytes)), 0, 0)
|
|
|
|
err = ignoreSuccess(err)
|
|
|
|
return bytes, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func getVolumeSerialNumber(path string) (string, error) {
|
|
|
|
path16, err := windows.UTF16PtrFromString(path)
|
|
|
|
if err != nil {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var volumePath [1024]uint16
|
|
|
|
err = windows.GetVolumePathName(path16, &volumePath[0], uint32(len(volumePath)))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
var volumeSerial uint32
|
|
|
|
|
|
|
|
err = windows.GetVolumeInformation(
|
|
|
|
&volumePath[0],
|
|
|
|
nil, 0, // volume name buffer
|
|
|
|
&volumeSerial,
|
|
|
|
nil, // maximum component length
|
|
|
|
nil, // filesystem flags
|
|
|
|
nil, 0, // filesystem name buffer
|
|
|
|
)
|
|
|
|
err = ignoreSuccess(err)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("%08x", volumeSerial), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// windows api occasionally returns
|
|
|
|
func ignoreSuccess(err error) error {
|
|
|
|
if err == syscall.Errno(0) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|