check: Add one-way argument

--one-way argument will check that all files on source matches the files on detination,
but not the other way. For example files present on destination but not on source will not
trigger an error.

Fixes: #1526
This commit is contained in:
Kasper Byrdal Nielsen 2018-05-29 19:07:04 +02:00 committed by Nick Craig-Wood
parent 2a806a8d8b
commit aad75e6720
4 changed files with 45 additions and 19 deletions

View file

@ -9,11 +9,13 @@ import (
// Globals // Globals
var ( var (
download = false download = false
oneway = false
) )
func init() { func init() {
cmd.Root.AddCommand(commandDefintion) cmd.Root.AddCommand(commandDefintion)
commandDefintion.Flags().BoolVarP(&download, "download", "", download, "Check by downloading rather than with hash.") commandDefintion.Flags().BoolVarP(&download, "download", "", download, "Check by downloading rather than with hash.")
commandDefintion.Flags().BoolVarP(&oneway, "one-way", "", oneway, "Check one way only, source files must exist on remote")
} }
var commandDefintion = &cobra.Command{ var commandDefintion = &cobra.Command{
@ -31,15 +33,19 @@ If you supply the --download flag, it will download the data from
both remotes and check them against each other on the fly. This can both remotes and check them against each other on the fly. This can
be useful for remotes that don't support hashes or if you really want be useful for remotes that don't support hashes or if you really want
to check all the data. to check all the data.
If you supply the --one-way flag, it will only check that files in source
match the files in destination, not the other way around. Meaning extra files in
destination that are not in the source will not trigger an error.
`, `,
Run: func(command *cobra.Command, args []string) { Run: func(command *cobra.Command, args []string) {
cmd.CheckArgs(2, 2, command, args) cmd.CheckArgs(2, 2, command, args)
fsrc, fdst := cmd.NewFsSrcDst(args) fsrc, fdst := cmd.NewFsSrcDst(args)
cmd.Run(false, false, command, func() error { cmd.Run(false, false, command, func() error {
if download { if download {
return operations.CheckDownload(fdst, fsrc) return operations.CheckDownload(fdst, fsrc, oneway)
} }
return operations.Check(fdst, fsrc) return operations.Check(fdst, fsrc, oneway)
}) })
}, },
} }

View file

@ -10,8 +10,14 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// Globals
var (
oneway = false
)
func init() { func init() {
cmd.Root.AddCommand(commandDefintion) cmd.Root.AddCommand(commandDefintion)
commandDefintion.Flags().BoolVarP(&oneway, "one-way", "", oneway, "Check one way only, source files must exist on destination")
} }
var commandDefintion = &cobra.Command{ var commandDefintion = &cobra.Command{
@ -40,6 +46,10 @@ the files in remote:path.
rclone cryptcheck remote:path encryptedremote:path rclone cryptcheck remote:path encryptedremote:path
After it has run it will log the status of the encryptedremote:. After it has run it will log the status of the encryptedremote:.
If you supply the --one-way flag, it will only check that files in source
match the files in destination, not the other way around. Meaning extra files in
destination that are not in the source will not trigger an error.
`, `,
Run: func(command *cobra.Command, args []string) { Run: func(command *cobra.Command, args []string) {
cmd.CheckArgs(2, 2, command, args) cmd.CheckArgs(2, 2, command, args)
@ -100,5 +110,5 @@ func cryptCheck(fdst, fsrc fs.Fs) error {
return false, false return false, false
} }
return operations.CheckFn(fcrypt, fsrc, checkIdentical) return operations.CheckFn(fcrypt, fsrc, checkIdentical, oneway)
} }

View file

@ -561,6 +561,7 @@ type checkFn func(a, b fs.Object) (differ bool, noHash bool)
type checkMarch struct { type checkMarch struct {
fdst, fsrc fs.Fs fdst, fsrc fs.Fs
check checkFn check checkFn
oneway bool
differences int32 differences int32
noHashes int32 noHashes int32
srcFilesMissing int32 srcFilesMissing int32
@ -571,6 +572,9 @@ type checkMarch struct {
func (c *checkMarch) DstOnly(dst fs.DirEntry) (recurse bool) { func (c *checkMarch) DstOnly(dst fs.DirEntry) (recurse bool) {
switch dst.(type) { switch dst.(type) {
case fs.Object: case fs.Object:
if c.oneway {
return false
}
err := errors.Errorf("File not in %v", c.fsrc) err := errors.Errorf("File not in %v", c.fsrc)
fs.Errorf(dst, "%v", err) fs.Errorf(dst, "%v", err)
fs.CountError(err) fs.CountError(err)
@ -666,11 +670,12 @@ func (c *checkMarch) Match(dst, src fs.DirEntry) (recurse bool) {
// //
// it returns true if differences were found // it returns true if differences were found
// it also returns whether it couldn't be hashed // it also returns whether it couldn't be hashed
func CheckFn(fdst, fsrc fs.Fs, check checkFn) error { func CheckFn(fdst, fsrc fs.Fs, check checkFn, oneway bool) error {
c := &checkMarch{ c := &checkMarch{
fdst: fdst, fdst: fdst,
fsrc: fsrc, fsrc: fsrc,
check: check, check: check,
oneway: oneway,
} }
// set up a march over fdst and fsrc // set up a march over fdst and fsrc
@ -696,8 +701,8 @@ func CheckFn(fdst, fsrc fs.Fs, check checkFn) error {
} }
// Check the files in fsrc and fdst according to Size and hash // Check the files in fsrc and fdst according to Size and hash
func Check(fdst, fsrc fs.Fs) error { func Check(fdst, fsrc fs.Fs, oneway bool) error {
return CheckFn(fdst, fsrc, checkIdentical) return CheckFn(fdst, fsrc, checkIdentical, oneway)
} }
// CheckEqualReaders checks to see if in1 and in2 have the same // CheckEqualReaders checks to see if in1 and in2 have the same
@ -754,7 +759,7 @@ func CheckIdentical(dst, src fs.Object) (differ bool, err error) {
// CheckDownload checks the files in fsrc and fdst according to Size // CheckDownload checks the files in fsrc and fdst according to Size
// and the actual contents of the files. // and the actual contents of the files.
func CheckDownload(fdst, fsrc fs.Fs) error { func CheckDownload(fdst, fsrc fs.Fs, oneway bool) error {
check := func(a, b fs.Object) (differ bool, noHash bool) { check := func(a, b fs.Object) (differ bool, noHash bool) {
differ, err := CheckIdentical(a, b) differ, err := CheckIdentical(a, b)
if err != nil { if err != nil {
@ -764,7 +769,7 @@ func CheckDownload(fdst, fsrc fs.Fs) error {
} }
return differ, false return differ, false
} }
return CheckFn(fdst, fsrc, check) return CheckFn(fdst, fsrc, check, oneway)
} }
// ListFn lists the Fs to the supplied function // ListFn lists the Fs to the supplied function

View file

@ -238,14 +238,14 @@ func TestDelete(t *testing.T) {
fstest.CheckItems(t, r.Fremote, file3) fstest.CheckItems(t, r.Fremote, file3)
} }
func testCheck(t *testing.T, checkFunction func(fdst, fsrc fs.Fs) error) { func testCheck(t *testing.T, checkFunction func(fdst, fsrc fs.Fs, oneway bool) error) {
r := fstest.NewRun(t) r := fstest.NewRun(t)
defer r.Finalise() defer r.Finalise()
check := func(i int, wantErrors int64) { check := func(i int, wantErrors int64, oneway bool) {
fs.Debugf(r.Fremote, "%d: Starting check test", i) fs.Debugf(r.Fremote, "%d: Starting check test", i)
oldErrors := accounting.Stats.GetErrors() oldErrors := accounting.Stats.GetErrors()
err := checkFunction(r.Flocal, r.Fremote) err := checkFunction(r.Fremote, r.Flocal, oneway)
gotErrors := accounting.Stats.GetErrors() - oldErrors gotErrors := accounting.Stats.GetErrors() - oldErrors
if wantErrors == 0 && err != nil { if wantErrors == 0 && err != nil {
t.Errorf("%d: Got error when not expecting one: %v", i, err) t.Errorf("%d: Got error when not expecting one: %v", i, err)
@ -262,15 +262,15 @@ func testCheck(t *testing.T, checkFunction func(fdst, fsrc fs.Fs) error) {
file1 := r.WriteBoth("rutabaga", "is tasty", t3) file1 := r.WriteBoth("rutabaga", "is tasty", t3)
fstest.CheckItems(t, r.Fremote, file1) fstest.CheckItems(t, r.Fremote, file1)
fstest.CheckItems(t, r.Flocal, file1) fstest.CheckItems(t, r.Flocal, file1)
check(1, 0) check(1, 0, false)
file2 := r.WriteFile("potato2", "------------------------------------------------------------", t1) file2 := r.WriteFile("potato2", "------------------------------------------------------------", t1)
fstest.CheckItems(t, r.Flocal, file1, file2) fstest.CheckItems(t, r.Flocal, file1, file2)
check(2, 1) check(2, 1, false)
file3 := r.WriteObject("empty space", "", t2) file3 := r.WriteObject("empty space", "", t2)
fstest.CheckItems(t, r.Fremote, file1, file3) fstest.CheckItems(t, r.Fremote, file1, file3)
check(3, 2) check(3, 2, false)
file2r := file2 file2r := file2
if fs.Config.SizeOnly { if fs.Config.SizeOnly {
@ -279,11 +279,16 @@ func testCheck(t *testing.T, checkFunction func(fdst, fsrc fs.Fs) error) {
r.WriteObject("potato2", "------------------------------------------------------------", t1) r.WriteObject("potato2", "------------------------------------------------------------", t1)
} }
fstest.CheckItems(t, r.Fremote, file1, file2r, file3) fstest.CheckItems(t, r.Fremote, file1, file2r, file3)
check(4, 1) check(4, 1, false)
r.WriteFile("empty space", "", t2) r.WriteFile("empty space", "", t2)
fstest.CheckItems(t, r.Flocal, file1, file2, file3) fstest.CheckItems(t, r.Flocal, file1, file2, file3)
check(5, 0) check(5, 0, false)
file4 := r.WriteObject("remotepotato", "------------------------------------------------------------", t1)
fstest.CheckItems(t, r.Fremote, file1, file2r, file3, file4)
check(6, 1, false)
check(7, 0, true)
} }
func TestCheck(t *testing.T) { func TestCheck(t *testing.T) {