package main import ( "errors" "fmt" "io/fs" "os" "path/filepath" "strconv" "strings" ) func readQuota(root string) int64 { b, err := os.ReadFile(filepath.Join(root, "quota")) if err != nil { return 0 } n, _ := parseSize(strings.TrimSpace(string(b))) return n } func parseSize(s string) (int64, error) { s = strings.ToUpper(strings.TrimSpace(s)) s = strings.TrimSpace(strings.TrimSuffix(strings.TrimSuffix(s, "B"), "I")) mult := int64(1) switch { case strings.HasSuffix(s, "G"): mult, s = 1<<30, strings.TrimSuffix(s, "G") case strings.HasSuffix(s, "M"): mult, s = 1<<20, strings.TrimSuffix(s, "M") case strings.HasSuffix(s, "K"): mult, s = 1<<10, strings.TrimSuffix(s, "K") } n, err := strconv.ParseInt(strings.TrimSpace(s), 10, 64) if err != nil || n < 0 { return 0, errors.New("size must be a number with an optional K, M or G suffix") } return n * mult, nil } func usage(dir string) int64 { var total int64 filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error { if err == nil && !d.IsDir() { if info, err := d.Info(); err == nil { total += info.Size() } } return nil }) return total } func overQuota(root string, owner string) (bool, int64, int64) { quota := readQuota(root) if quota == 0 { return false, 0, 0 } used := usage(filepath.Join(root, "repos", owner)) return used >= quota, used, quota } func quotaMessage(used, quota int64) string { return fmt.Sprintf("disk quota exceeded, %s used of %s", humanSize(used), humanSize(quota)) } func writeQuota(root string, n int64) error { return os.WriteFile(filepath.Join(root, "quota"), []byte(strconv.FormatInt(n, 10)+"\n"), 0o644) } func pushLimit(root, owner string) []string { if over, used, quota := overQuota(root, owner); quota > 0 && !over { return []string{"-c", "receive.maxInputSize=" + strconv.FormatInt(quota-used, 10)} } return nil } func compactSize(n int64) string { for _, u := range []struct { suffix string size int64 }{{"G", 1 << 30}, {"M", 1 << 20}, {"K", 1 << 10}} { if n >= u.size && n%u.size == 0 { return strconv.FormatInt(n/u.size, 10) + u.suffix } } return strconv.FormatInt(n, 10) }