diff --git a/fs/config.go b/fs/config.go index a7c0e6f08..3617e98cd 100644 --- a/fs/config.go +++ b/fs/config.go @@ -33,6 +33,7 @@ type ConfigInfo struct { SizeOnly bool IgnoreTimes bool IgnoreExisting bool + IgnoreErrors bool ModifyWindow time.Duration Checkers int Transfers int diff --git a/fs/config/configflags/configflags.go b/fs/config/configflags/configflags.go index c93232eb3..623b9c98b 100644 --- a/fs/config/configflags/configflags.go +++ b/fs/config/configflags/configflags.go @@ -43,6 +43,7 @@ func AddFlags(flagSet *pflag.FlagSet) { flags.BoolVarP(flagSet, &fs.Config.SizeOnly, "size-only", "", fs.Config.SizeOnly, "Skip based on size only, not mod-time or checksum") flags.BoolVarP(flagSet, &fs.Config.IgnoreTimes, "ignore-times", "I", fs.Config.IgnoreTimes, "Don't skip files that match size and time - transfer all files") flags.BoolVarP(flagSet, &fs.Config.IgnoreExisting, "ignore-existing", "", fs.Config.IgnoreExisting, "Skip all files that exist on destination") + flags.BoolVarP(flagSet, &fs.Config.IgnoreErrors, "ignore-errors", "", fs.Config.IgnoreErrors, "delete even if there are I/O errors") flags.BoolVarP(flagSet, &fs.Config.DryRun, "dry-run", "n", fs.Config.DryRun, "Do a trial run with no permanent changes") flags.DurationVarP(flagSet, &fs.Config.ConnectTimeout, "contimeout", "", fs.Config.ConnectTimeout, "Connect timeout") flags.DurationVarP(flagSet, &fs.Config.Timeout, "timeout", "", fs.Config.Timeout, "IO idle timeout") diff --git a/fs/sync/sync.go b/fs/sync/sync.go index dee90525c..405191f6e 100644 --- a/fs/sync/sync.go +++ b/fs/sync/sync.go @@ -399,7 +399,7 @@ func (s *syncCopyMove) stopDeleters() { // checkSrcMap is clear then it assumes that the any source files that // have been found have been removed from dstFiles already. func (s *syncCopyMove) deleteFiles(checkSrcMap bool) error { - if accounting.Stats.Errored() { + if accounting.Stats.Errored() && !fs.Config.IgnoreErrors { fs.Errorf(s.fdst, "%v", fs.ErrorNotDeleting) return fs.ErrorNotDeleting } @@ -430,7 +430,7 @@ func deleteEmptyDirectories(f fs.Fs, entries fs.DirEntries) error { if len(entries) == 0 { return nil } - if accounting.Stats.Errored() { + if accounting.Stats.Errored() && !fs.Config.IgnoreErrors { fs.Errorf(f, "%v", fs.ErrorNotDeletingDirs) return fs.ErrorNotDeletingDirs } @@ -624,7 +624,7 @@ func (s *syncCopyMove) run() error { // Delete files after if s.deleteMode == fs.DeleteModeAfter { - if s.currentError() != nil { + if s.currentError() != nil && !fs.Config.IgnoreErrors { fs.Errorf(s.fdst, "%v", fs.ErrorNotDeleting) } else { s.processError(s.deleteFiles(false)) @@ -633,7 +633,7 @@ func (s *syncCopyMove) run() error { // Prune empty directories if s.deleteMode != fs.DeleteModeOff { - if s.currentError() != nil { + if s.currentError() != nil && !fs.Config.IgnoreErrors { fs.Errorf(s.fdst, "%v", fs.ErrorNotDeletingDirs) } else { s.processError(deleteEmptyDirectories(s.fdst, s.dstEmptyDirs)) diff --git a/fs/sync/sync_test.go b/fs/sync/sync_test.go index 687bd67e8..e88990c84 100644 --- a/fs/sync/sync_test.go +++ b/fs/sync/sync_test.go @@ -283,6 +283,78 @@ func TestSyncIgnoreExisting(t *testing.T) { fstest.CheckItems(t, r.Fremote, file1) } +func TestSyncIgnoreErrors(t *testing.T) { + r := fstest.NewRun(t) + fs.Config.IgnoreErrors = true + defer func() { + fs.Config.IgnoreErrors = false + r.Finalise() + }() + file1 := r.WriteFile("a/potato2", "------------------------------------------------------------", t1) + file2 := r.WriteObject("b/potato", "SMALLER BUT SAME DATE", t2) + file3 := r.WriteBoth("c/non empty space", "AhHa!", t2) + require.NoError(t, operations.Mkdir(r.Fremote, "d")) + + fstest.CheckListingWithPrecision( + t, + r.Flocal, + []fstest.Item{ + file1, + file3, + }, + []string{ + "a", + "c", + }, + fs.Config.ModifyWindow, + ) + fstest.CheckListingWithPrecision( + t, + r.Fremote, + []fstest.Item{ + file2, + file3, + }, + []string{ + "b", + "c", + "d", + }, + fs.Config.ModifyWindow, + ) + + accounting.Stats.ResetCounters() + fs.CountError(nil) + assert.NoError(t, Sync(r.Fremote, r.Flocal)) + + fstest.CheckListingWithPrecision( + t, + r.Flocal, + []fstest.Item{ + file1, + file3, + }, + []string{ + "a", + "c", + }, + fs.Config.ModifyWindow, + ) + fstest.CheckListingWithPrecision( + t, + r.Fremote, + []fstest.Item{ + file1, + file3, + }, + []string{ + "a", + "c", + }, + fs.Config.ModifyWindow, + ) +} + func TestSyncAfterChangingModtimeOnly(t *testing.T) { r := fstest.NewRun(t) defer r.Finalise()