Jelajahi Sumber

lib/model: Don't stop folder if out of disk space (fixes #2370) (#5099)

This removes the out of disk space check from CheckHealth. The disk space is now
only checked if there are files to pull, in which case pulling those files is
stopped, but everything else (dirs, links, deletes) keeps running -> can recover
disk space through pulling.
Simon Frei 7 tahun lalu
induk
melakukan
cb0f4ce55a

+ 13 - 2
lib/config/folderconfiguration.go

@@ -282,8 +282,19 @@ func (l FolderDeviceConfigurationList) Len() int {
 	return len(l)
 	return len(l)
 }
 }
 
 
-func (f *FolderConfiguration) CheckFreeSpace() error {
-	return checkFreeSpace(f.MinDiskFree, f.Filesystem())
+func (f *FolderConfiguration) CheckAvailableSpace(req int64) error {
+	fs := f.Filesystem()
+	usage, err := fs.Usage(".")
+	if err != nil {
+		return nil
+	}
+	usage.Free -= req
+	if usage.Free > 0 {
+		if err := checkFreeSpace(f.MinDiskFree, usage); err == nil {
+			return nil
+		}
+	}
+	return fmt.Errorf("insufficient space in %v %v", fs.Type(), fs.URI())
 }
 }
 
 
 type FolderConfigurationList []FolderConfiguration
 type FolderConfigurationList []FolderConfiguration

+ 5 - 8
lib/config/size.go

@@ -76,22 +76,19 @@ func (Size) ParseDefault(s string) (interface{}, error) {
 	return ParseSize(s)
 	return ParseSize(s)
 }
 }
 
 
-func checkFreeSpace(req Size, fs fs.Filesystem) error {
+func checkFreeSpace(req Size, usage fs.Usage) error {
 	val := req.BaseValue()
 	val := req.BaseValue()
 	if val <= 0 {
 	if val <= 0 {
 		return nil
 		return nil
 	}
 	}
 
 
-	usage, err := fs.Usage(".")
 	if req.Percentage() {
 	if req.Percentage() {
 		freePct := (float64(usage.Free) / float64(usage.Total)) * 100
 		freePct := (float64(usage.Free) / float64(usage.Total)) * 100
-		if err == nil && freePct < val {
-			return fmt.Errorf("insufficient space in %v %v: %f %% < %v", fs.Type(), fs.URI(), freePct, req)
-		}
-	} else {
-		if err == nil && float64(usage.Free) < val {
-			return fmt.Errorf("insufficient space in %v %v: %v < %v", fs.Type(), fs.URI(), usage.Free, req)
+		if freePct < val {
+			return fmt.Errorf("%f %% < %v", freePct, req)
 		}
 		}
+	} else if float64(usage.Free) < val {
+		return fmt.Errorf("%v < %v", usage.Free, req)
 	}
 	}
 
 
 	return nil
 	return nil

+ 4 - 1
lib/config/wrapper.go

@@ -445,5 +445,8 @@ func (w *Wrapper) MyName() string {
 // CheckHomeFreeSpace returns nil if the home disk has the required amount of
 // CheckHomeFreeSpace returns nil if the home disk has the required amount of
 // free space, or if home disk free space checking is disabled.
 // free space, or if home disk free space checking is disabled.
 func (w *Wrapper) CheckHomeFreeSpace() error {
 func (w *Wrapper) CheckHomeFreeSpace() error {
-	return checkFreeSpace(w.Options().MinHomeDiskFree, fs.NewFilesystem(fs.FilesystemTypeBasic, filepath.Dir(w.ConfigPath())))
+	if usage, err := fs.NewFilesystem(fs.FilesystemTypeBasic, filepath.Dir(w.ConfigPath())).Usage("."); err == nil {
+		return checkFreeSpace(w.Options().MinHomeDiskFree, usage)
+	}
+	return nil
 }
 }

+ 0 - 4
lib/model/folder.go

@@ -254,10 +254,6 @@ func (f *folder) getHealthError() error {
 		return err
 		return err
 	}
 	}
 
 
-	if err := f.CheckFreeSpace(); err != nil {
-		return err
-	}
-
 	if err := f.model.cfg.CheckHomeFreeSpace(); err != nil {
 	if err := f.model.cfg.CheckHomeFreeSpace(); err != nil {
 		return err
 		return err
 	}
 	}

+ 8 - 8
lib/model/folder_sendrecv.go

@@ -839,9 +839,12 @@ func (f *sendReceiveFolder) renameFile(source, target protocol.FileInfo, dbUpdat
 	l.Debugln(f, "taking rename shortcut", source.Name, "->", target.Name)
 	l.Debugln(f, "taking rename shortcut", source.Name, "->", target.Name)
 
 
 	if f.versioner != nil {
 	if f.versioner != nil {
-		err = osutil.Copy(f.fs, source.Name, target.Name)
+		err = f.CheckAvailableSpace(source.Size)
 		if err == nil {
 		if err == nil {
-			err = osutil.InWritableDir(f.versioner.Archive, f.fs, source.Name)
+			err = osutil.Copy(f.fs, source.Name, target.Name)
+			if err == nil {
+				err = osutil.InWritableDir(f.versioner.Archive, f.fs, source.Name)
+			}
 		}
 		}
 	} else {
 	} else {
 		err = osutil.TryRename(f.fs, source.Name, target.Name)
 		err = osutil.TryRename(f.fs, source.Name, target.Name)
@@ -1003,12 +1006,9 @@ func (f *sendReceiveFolder) handleFile(file protocol.FileInfo, copyChan chan<- c
 		blocksSize = file.Size
 		blocksSize = file.Size
 	}
 	}
 
 
-	if f.MinDiskFree.BaseValue() > 0 {
-		if usage, err := f.fs.Usage("."); err == nil && usage.Free < blocksSize {
-			l.Warnf(`Folder "%s": insufficient disk space in %s for %s: have %.2f MiB, need %.2f MiB`, f.folderID, f.fs.URI(), file.Name, float64(usage.Free)/1024/1024, float64(blocksSize)/1024/1024)
-			f.newError("disk space", file.Name, errors.New("insufficient space"))
-			return
-		}
+	if err := f.CheckAvailableSpace(blocksSize); err != nil {
+		f.newError("pulling file", file.Name, err)
+		return
 	}
 	}
 
 
 	// Shuffle the blocks
 	// Shuffle the blocks

+ 3 - 1
lib/model/folder_sendrecv_test.go

@@ -91,15 +91,17 @@ func setUpSendReceiveFolder(model *Model) *sendReceiveFolder {
 			initialScanFinished: make(chan struct{}),
 			initialScanFinished: make(chan struct{}),
 			ctx:                 context.TODO(),
 			ctx:                 context.TODO(),
 			FolderConfiguration: config.FolderConfiguration{
 			FolderConfiguration: config.FolderConfiguration{
+				FilesystemType:      fs.FilesystemTypeBasic,
+				Path:                "testdata",
 				PullerMaxPendingKiB: defaultPullerPendingKiB,
 				PullerMaxPendingKiB: defaultPullerPendingKiB,
 			},
 			},
 		},
 		},
 
 
-		fs:        fs.NewMtimeFS(fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata"), db.NewNamespacedKV(model.db, "mtime")),
 		queue:     newJobQueue(),
 		queue:     newJobQueue(),
 		errors:    make(map[string]string),
 		errors:    make(map[string]string),
 		errorsMut: sync.NewMutex(),
 		errorsMut: sync.NewMutex(),
 	}
 	}
+	f.fs = fs.NewMtimeFS(f.Filesystem(), db.NewNamespacedKV(model.db, "mtime"))
 
 
 	// Folders are never actually started, so no initial scan will be done
 	// Folders are never actually started, so no initial scan will be done
 	close(f.initialScanFinished)
 	close(f.initialScanFinished)