110 lines
2.5 KiB
Go
110 lines
2.5 KiB
Go
|
package utils
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"golang.org/x/sys/unix"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"syscall"
|
||
|
|
||
|
"github.com/docker/docker/pkg/mount"
|
||
|
)
|
||
|
|
||
|
// GetDiskUsageStats accepts a path to a directory or file
|
||
|
// and returns the number of bytes and inodes used by the path
|
||
|
func GetDiskUsageStats(path string) (uint64, uint64, error) {
|
||
|
var dirSize, inodeCount uint64
|
||
|
|
||
|
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||
|
fileStat, error := os.Lstat(path)
|
||
|
if error != nil {
|
||
|
if fileStat.Mode()&os.ModeSymlink != 0 {
|
||
|
// Is a symlink; no error should be returned
|
||
|
return nil
|
||
|
}
|
||
|
return error
|
||
|
}
|
||
|
|
||
|
dirSize += uint64(info.Size())
|
||
|
inodeCount++
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
if err != nil {
|
||
|
return 0, 0, err
|
||
|
}
|
||
|
|
||
|
return dirSize, inodeCount, err
|
||
|
}
|
||
|
|
||
|
// GetDeviceUUIDFromPath accepts a path, and will find the device
|
||
|
// corresponding to the path and return the UUID of that device
|
||
|
func GetDeviceUUIDFromPath(devicePath string) (string, error) {
|
||
|
const dir = "/dev/disk/by-uuid"
|
||
|
|
||
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||
|
return "", nil
|
||
|
}
|
||
|
|
||
|
files, err := ioutil.ReadDir(dir)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
for _, file := range files {
|
||
|
path := filepath.Join(dir, file.Name())
|
||
|
target, err := os.Readlink(path)
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
device, err := filepath.Abs(filepath.Join(dir, target))
|
||
|
if err != nil {
|
||
|
return "", fmt.Errorf("failed to resolve the absolute path of %q", filepath.Join(dir, target))
|
||
|
}
|
||
|
if strings.Compare(device, devicePath) == 0 {
|
||
|
return file.Name(), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return "", fmt.Errorf("device path %s not found", devicePath)
|
||
|
}
|
||
|
|
||
|
// GetStattFromPath is a helper function that returns the Stat_t
|
||
|
// object for a given path
|
||
|
func GetStattFromPath(path string) (syscall.Stat_t, error) {
|
||
|
statInfo := syscall.Stat_t{}
|
||
|
err := syscall.Lstat(path, &statInfo)
|
||
|
if err != nil {
|
||
|
return statInfo, err
|
||
|
}
|
||
|
return statInfo, nil
|
||
|
}
|
||
|
|
||
|
// GetDeviceNameFromPath iterates through the mounts and matches
|
||
|
// the one that the provided path is on
|
||
|
func GetDeviceNameFromPath(path string) (string, error) {
|
||
|
statInfo, err := GetStattFromPath(path)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
mounts, err := mount.GetMounts()
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
queryMajor := int(unix.Major(uint64(statInfo.Dev)))
|
||
|
queryMinor := int(unix.Minor(uint64(statInfo.Dev)))
|
||
|
|
||
|
for _, mount := range mounts {
|
||
|
if mount.Minor == queryMinor && mount.Major == queryMajor {
|
||
|
return mount.Source, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return "", fmt.Errorf("no match found")
|
||
|
}
|