@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user