//go:build linux package system import ( "fmt" "os" "path/filepath" "strings" "syscall" ) // IsMountPoint checks if a path is on a different device than its parent. // Returns true if the path is a mount point (different device ID from parent). func IsMountPoint(path string) bool { var pathStat, parentStat syscall.Stat_t if err := syscall.Stat(path, &pathStat); err != nil { return false } parent := filepath.Dir(path) if err := syscall.Stat(parent, &parentStat); err != nil { return false } return pathStat.Dev != parentStat.Dev } // IsWritable checks if the given path is writable by attempting to create+remove a temp file. func IsWritable(path string) bool { testFile := filepath.Join(path, ".felhom-write-test") f, err := os.Create(testFile) if err != nil { return false } f.Close() os.Remove(testFile) return true } // PathsOverlap returns true if one path is a parent or child of the other. func PathsOverlap(a, b string) bool { a = filepath.Clean(a) b = filepath.Clean(b) if a == b { return true } aSep := a + string(os.PathSeparator) bSep := b + string(os.PathSeparator) return strings.HasPrefix(aSep, bSep) || strings.HasPrefix(bSep, aSep) } // DiskUsageInfo holds disk usage statistics for a path. type DiskUsageInfo struct { TotalGB float64 UsedGB float64 AvailGB float64 UsedPercent float64 TotalHuman string UsedHuman string } // GetDiskUsage returns disk usage info for a path, or nil on error. func GetDiskUsage(path string) *DiskUsageInfo { var stat syscall.Statfs_t if err := syscall.Statfs(path, &stat); err != nil { return nil } bsize := uint64(stat.Bsize) total := stat.Blocks * bsize avail := stat.Bavail * bsize used := total - (stat.Bfree * bsize) const gb = 1024 * 1024 * 1024 info := &DiskUsageInfo{ TotalGB: float64(total) / float64(gb), UsedGB: float64(used) / float64(gb), AvailGB: float64(avail) / float64(gb), } if total > 0 { info.UsedPercent = float64(used) / float64(total) * 100 } info.TotalHuman = formatGB(info.TotalGB) info.UsedHuman = formatGB(info.UsedGB) return info } func formatGB(gb float64) string { if gb >= 1000 { return fmt.Sprintf("%.1f TB", gb/1024) } return fmt.Sprintf("%.1f GB", gb) }