Files
jukebox_maker/internal/disk/disk.go

125 lines
2.5 KiB
Go

package disk
import (
"errors"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/google/uuid"
)
type DiskState string
const (
DiskAbsent DiskState = "absent"
DiskForeign DiskState = "foreign"
DiskKnown DiskState = "known"
)
type DiskInfo struct {
State DiskState `json:"state"`
DiskID string `json:"disk_id"`
TotalBytes int64 `json:"total_bytes"`
FreeBytes int64 `json:"free_bytes"`
MountPath string `json:"mount_path"`
}
const MarkerDir = ".jukebox"
const idFile = "disk.id"
func Probe(mountPath string) (DiskInfo, error) {
info := DiskInfo{MountPath: mountPath, State: DiskAbsent}
if _, err := os.ReadDir(mountPath); err != nil {
return info, nil
}
total, free, err := DiskUsage(mountPath)
if err != nil {
return info, nil
}
info.TotalBytes = total
info.FreeBytes = free
idPath := filepath.Join(mountPath, MarkerDir, idFile)
data, err := os.ReadFile(idPath)
if errors.Is(err, os.ErrNotExist) {
info.State = DiskForeign
return info, nil
}
if err != nil {
info.State = DiskForeign
return info, nil
}
info.DiskID = strings.TrimSpace(string(data))
info.State = DiskKnown
return info, nil
}
func IsMountPoint(path string) bool {
pathInfo, err := os.Stat(path)
if err != nil {
return false
}
parent := filepath.Dir(filepath.Clean(path))
parentInfo, err := os.Stat(parent)
if err != nil {
return false
}
pathStat, ok := pathInfo.Sys().(*syscall.Stat_t)
if !ok {
return false
}
parentStat, ok := parentInfo.Sys().(*syscall.Stat_t)
if !ok {
return false
}
return pathStat.Dev != parentStat.Dev
}
func CheckWritable(path string) error {
f, err := os.CreateTemp(path, ".jukebox-writecheck-*")
if err != nil {
return err
}
name := f.Name()
if err := f.Close(); err != nil {
_ = os.Remove(name)
return err
}
return os.Remove(name)
}
func InitDisk(mountPath string) (string, error) {
dir := filepath.Join(mountPath, MarkerDir)
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", err
}
id := uuid.New().String()
idPath := filepath.Join(dir, idFile)
if err := os.WriteFile(idPath, []byte(id), 0o644); err != nil {
return "", err
}
return id, nil
}
func DBPath(mountPath string) string {
return filepath.Join(mountPath, MarkerDir, "history.db")
}
func DiskUsage(mountPath string) (total, free int64, err error) {
var stat syscall.Statfs_t
if err = syscall.Statfs(mountPath, &stat); err != nil {
return 0, 0, err
}
total = int64(stat.Blocks) * int64(stat.Bsize)
free = int64(stat.Bavail) * int64(stat.Bsize)
return total, free, nil
}