lib: Use uint64 for disk stats (ref #3930) (#7019)

This commit is contained in:
Simon Frei
2020-10-02 15:22:28 +02:00
committed by GitHub
parent a20c6ca868
commit 48da6f0f22
6 changed files with 62 additions and 20 deletions
+4 -7
View File
@@ -235,7 +235,7 @@ func (f *FolderConfiguration) SharedWith(device protocol.DeviceID) bool {
return false return false
} }
func (f *FolderConfiguration) CheckAvailableSpace(req int64) error { func (f *FolderConfiguration) CheckAvailableSpace(req uint64) error {
val := f.MinDiskFree.BaseValue() val := f.MinDiskFree.BaseValue()
if val <= 0 { if val <= 0 {
return nil return nil
@@ -245,11 +245,8 @@ func (f *FolderConfiguration) CheckAvailableSpace(req int64) error {
if err != nil { if err != nil {
return nil return nil
} }
usage.Free -= req if !checkAvailableSpace(req, f.MinDiskFree, usage) {
if usage.Free > 0 { return fmt.Errorf("insufficient space in %v %v", fs.Type(), fs.URI())
if err := CheckFreeSpace(f.MinDiskFree, usage); err == nil {
return nil
}
} }
return fmt.Errorf("insufficient space in %v %v", fs.Type(), fs.URI()) return nil
} }
+17 -6
View File
@@ -73,25 +73,36 @@ func (s *Size) ParseDefault(str string) error {
return err return err
} }
func CheckFreeSpace(req Size, usage fs.Usage) error { // CheckFreeSpace checks that the free space does not fall below the minimum required free space.
val := req.BaseValue() func CheckFreeSpace(minFree Size, usage fs.Usage) error {
val := minFree.BaseValue()
if val <= 0 { if val <= 0 {
return nil return nil
} }
if req.Percentage() { if minFree.Percentage() {
freePct := (float64(usage.Free) / float64(usage.Total)) * 100 freePct := (float64(usage.Free) / float64(usage.Total)) * 100
if freePct < val { if freePct < val {
return fmt.Errorf("%.1f %% < %v", freePct, req) return fmt.Errorf("%.1f %% < %v", freePct, minFree)
} }
} else if float64(usage.Free) < val { } else if float64(usage.Free) < val {
return fmt.Errorf("%sB < %v", formatSI(usage.Free), req) return fmt.Errorf("%sB < %v", formatSI(usage.Free), minFree)
} }
return nil return nil
} }
func formatSI(b int64) string { // checkAvailableSpace checks that the free space does not fall below the minimum
// required free space, considering additional required space for a future operation.
func checkAvailableSpace(req uint64, minFree Size, usage fs.Usage) bool {
if usage.Free < req {
return false
}
usage.Free -= req
return CheckFreeSpace(minFree, usage) == nil
}
func formatSI(b uint64) string {
switch { switch {
case b < 1000: case b < 1000:
return fmt.Sprintf("%d ", b) return fmt.Sprintf("%d ", b)
+35 -1
View File
@@ -9,6 +9,7 @@ package config
import ( import (
"testing" "testing"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/util" "github.com/syncthing/syncthing/lib/util"
) )
@@ -66,6 +67,10 @@ func TestParseSize(t *testing.T) {
// The empty string is a valid zero // The empty string is a valid zero
{"", true, 0, false}, {"", true, 0, false},
{" ", true, 0, false}, {" ", true, 0, false},
// Just numbers are fine too
{"0", true, 0, false},
{"3", true, 3, false},
{"34.3", true, 34.3, false},
} }
for _, tc := range cases { for _, tc := range cases {
@@ -94,7 +99,7 @@ func TestParseSize(t *testing.T) {
func TestFormatSI(t *testing.T) { func TestFormatSI(t *testing.T) {
cases := []struct { cases := []struct {
bytes int64 bytes uint64
result string result string
}{ }{
{ {
@@ -130,3 +135,32 @@ func TestFormatSI(t *testing.T) {
} }
} }
} }
func TestCheckAvailableSize(t *testing.T) {
cases := []struct {
req, free, total uint64
minFree string
ok bool
}{
{10, 1e8, 1e9, "1%", true},
{1e4, 1e3, 1e9, "1%", false},
{1e2, 1e3, 1e9, "1%", false},
{1e9, 1 << 62, 1 << 63, "1%", true},
{10, 1e8, 1e9, "1M", true},
{1e4, 1e3, 1e9, "1M", false},
{1e2, 1e3, 1e9, "1M", false},
{1e9, 1 << 62, 1 << 63, "1M", true},
}
for _, tc := range cases {
minFree, err := ParseSize(tc.minFree)
if err != nil {
t.Errorf("Failed to parse %v: %v", tc.minFree, err)
continue
}
usage := fs.Usage{Free: tc.free, Total: tc.total}
if ok := checkAvailableSpace(tc.req, minFree, usage); ok != tc.ok {
t.Errorf("checkAvailableSpace(%v, %v, %v) == %v, expected %v", tc.req, minFree, usage, ok, tc.ok)
}
}
}
+2 -2
View File
@@ -295,8 +295,8 @@ func (f *BasicFilesystem) Usage(name string) (Usage, error) {
return Usage{}, err return Usage{}, err
} }
return Usage{ return Usage{
Free: int64(u.Free), Free: u.Free,
Total: int64(u.Total), Total: u.Total,
}, nil }, nil
} }
+2 -2
View File
@@ -91,8 +91,8 @@ func (fm FileMode) String() string {
// Usage represents filesystem space usage // Usage represents filesystem space usage
type Usage struct { type Usage struct {
Free int64 Free uint64
Total int64 Total uint64
} }
type Matcher interface { type Matcher interface {
+2 -2
View File
@@ -995,7 +995,7 @@ func (f *sendReceiveFolder) renameFile(cur, source, target protocol.FileInfo, sn
tempName := fs.TempName(target.Name) tempName := fs.TempName(target.Name)
if f.versioner != nil { if f.versioner != nil {
err = f.CheckAvailableSpace(source.Size) err = f.CheckAvailableSpace(uint64(source.Size))
if err == nil { if err == nil {
err = osutil.Copy(f.CopyRangeMethod, f.fs, f.fs, source.Name, tempName) err = osutil.Copy(f.CopyRangeMethod, f.fs, f.fs, source.Name, tempName)
if err == nil { if err == nil {
@@ -1239,7 +1239,7 @@ func (f *sendReceiveFolder) copierRoutine(in <-chan copyBlocksState, pullChan ch
} }
for state := range in { for state := range in {
if err := f.CheckAvailableSpace(state.file.Size); err != nil { if err := f.CheckAvailableSpace(uint64(state.file.Size)); err != nil {
state.fail(err) state.fail(err)
// Nothing more to do for this failed file, since it would use to much disk space // Nothing more to do for this failed file, since it would use to much disk space
out <- state.sharedPullerState out <- state.sharedPullerState