diff --git a/amazonclouddrive/amazonclouddrive.go b/amazonclouddrive/amazonclouddrive.go index b9d8b5163..f382f921e 100644 --- a/amazonclouddrive/amazonclouddrive.go +++ b/amazonclouddrive/amazonclouddrive.go @@ -660,55 +660,90 @@ func (f *Fs) DirCacheFlush() { f.dirCache.ResetRoot() } -// DirMove moves src directory to this remote using server side move -// operations. +// DirMove moves src, srcRemote to this remote at dstRemote +// using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists -func (f *Fs) DirMove(src fs.Fs) (err error) { - // go test -v -run '^Test(Setup|Init|FsMkdir|FsPutFile1|FsPutFile2|FsUpdateFile1|FsDirMove)$ +func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) (err error) { srcFs, ok := src.(*Fs) if !ok { fs.Debugf(src, "DirMove error: not same remote type") return fs.ErrorCantDirMove } - - // Check if destination exists - if f.dirCache.FoundRoot() { - fs.Debugf(src, "DirMove error: destination exists") - return fs.ErrorDirExists - } + srcPath := path.Join(srcFs.root, srcRemote) + dstPath := path.Join(f.root, dstRemote) // Refuse to move to or from the root - if f.root == "" || srcFs.root == "" { + if srcPath == "" || dstPath == "" { fs.Debugf(src, "DirMove error: Can't move root") return errors.New("can't move root directory") } - // Find ID of parent - dstLeaf, dstDirectoryID, err := f.dirCache.FindPath(f.root, true) - if err != nil { - return err - } - - // Find the ID of the source and make a node from it + // find the root src directory err = srcFs.dirCache.FindRoot(false) if err != nil { - fs.Debugf(src, "DirMove error: error finding src root: %v", err) return err } - srcDirectoryID, err := srcFs.dirCache.RootParentID() + + // find the root dst directory + if dstRemote != "" { + err = f.dirCache.FindRoot(true) + if err != nil { + return err + } + } else { + if f.dirCache.FoundRoot() { + return fs.ErrorDirExists + } + } + + // Find ID of dst parent, creating subdirs if necessary + findPath := dstRemote + if dstRemote == "" { + findPath = f.root + } + dstLeaf, dstDirectoryID, err := f.dirCache.FindPath(findPath, true) + if err != nil { + return err + } + + // Check destination does not exist + if dstRemote != "" { + _, err = f.dirCache.FindDir(dstRemote, false) + if err == fs.ErrorDirNotFound { + // OK + } else if err != nil { + return err + } else { + return fs.ErrorDirExists + } + } + + // Find ID of src parent + findPath = srcRemote + var srcDirectoryID string + if srcRemote == "" { + srcDirectoryID, err = srcFs.dirCache.RootParentID() + } else { + _, srcDirectoryID, err = srcFs.dirCache.FindPath(findPath, false) + } + if err != nil { + return err + } + srcLeaf, _ := dircache.SplitPath(srcPath) + + // Find ID of src + srcID, err := srcFs.dirCache.FindDir(srcRemote, false) if err != nil { - fs.Debugf(src, "DirMove error: error finding src RootParentID: %v", err) return err } - srcLeaf, _ := dircache.SplitPath(srcFs.root) // FIXME make a proper node.UpdateMetadata command - srcInfo := acd.NodeFromId(srcFs.dirCache.RootID(), f.c.Nodes) + srcInfo := acd.NodeFromId(srcID, f.c.Nodes) var jsonStr string err = srcFs.pacer.Call(func() (bool, error) { jsonStr, err = srcInfo.GetMetadata() @@ -724,10 +759,13 @@ func (f *Fs) DirMove(src fs.Fs) (err error) { return err } - err = f.moveNode(srcFs.root, dstLeaf, dstDirectoryID, srcInfo, srcLeaf, srcDirectoryID, true) + err = f.moveNode(srcPath, dstLeaf, dstDirectoryID, srcInfo, srcLeaf, srcDirectoryID, true) + if err != nil { + return err + } - srcFs.dirCache.ResetRoot() - return err + srcFs.dirCache.FlushDir(srcRemote) + return nil } // purgeCheck remotes the root directory, if check is set then it diff --git a/crypt/crypt.go b/crypt/crypt.go index cb1a8f9ee..e99d4b734 100644 --- a/crypt/crypt.go +++ b/crypt/crypt.go @@ -254,15 +254,15 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) { return f.newObject(oResult), nil } -// DirMove moves src to this remote using server side move -// operations. +// DirMove moves src, srcRemote to this remote at dstRemote +// using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists -func (f *Fs) DirMove(src fs.Fs) error { +func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) error { do := f.Fs.Features().DirMove if do == nil { return fs.ErrorCantDirMove @@ -272,7 +272,7 @@ func (f *Fs) DirMove(src fs.Fs) error { fs.Debugf(srcFs, "Can't move directory - not same remote type") return fs.ErrorCantDirMove } - return do(srcFs.Fs) + return do(srcFs.Fs, f.cipher.EncryptDirName(srcRemote), f.cipher.EncryptDirName(dstRemote)) } // PutUnchecked uploads the object diff --git a/drive/drive.go b/drive/drive.go index d194e6cb8..326a0397d 100644 --- a/drive/drive.go +++ b/drive/drive.go @@ -791,34 +791,72 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) { return dstObj, nil } -// DirMove moves src directory to this remote using server side move -// operations. +// DirMove moves src, srcRemote to this remote at dstRemote +// using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists -func (f *Fs) DirMove(src fs.Fs) error { +func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) error { srcFs, ok := src.(*Fs) if !ok { fs.Debugf(srcFs, "Can't move directory - not same remote type") return fs.ErrorCantDirMove } - - // Check if destination exists - if f.dirCache.FoundRoot() { - return fs.ErrorDirExists - } + srcPath := path.Join(srcFs.root, srcRemote) + dstPath := path.Join(f.root, dstRemote) // Refuse to move to or from the root - if f.root == "" || srcFs.root == "" { + if srcPath == "" || dstPath == "" { fs.Debugf(src, "DirMove error: Can't move root") return errors.New("can't move root directory") } - // Find ID of parent - leaf, directoryID, err := f.dirCache.FindPath(f.root, true) + // find the root src directory + err := srcFs.dirCache.FindRoot(false) + if err != nil { + return err + } + + // find the root dst directory + if dstRemote != "" { + err = f.dirCache.FindRoot(true) + if err != nil { + return err + } + } else { + if f.dirCache.FoundRoot() { + return fs.ErrorDirExists + } + } + + // Find ID of dst parent, creating subdirs if necessary + var leaf, directoryID string + findPath := dstRemote + if dstRemote == "" { + findPath = f.root + } + leaf, directoryID, err = f.dirCache.FindPath(findPath, true) + if err != nil { + return err + } + + // Check destination does not exist + if dstRemote != "" { + _, err = f.dirCache.FindDir(dstRemote, false) + if err == fs.ErrorDirNotFound { + // OK + } else if err != nil { + return err + } else { + return fs.ErrorDirExists + } + } + + // Find ID of src + srcID, err := srcFs.dirCache.FindDir(srcRemote, false) if err != nil { return err } @@ -829,13 +867,13 @@ func (f *Fs) DirMove(src fs.Fs) error { Parents: []*drive.ParentReference{{Id: directoryID}}, } err = f.pacer.Call(func() (bool, error) { - _, err = f.svc.Files.Patch(srcFs.dirCache.RootID(), &patch).Do() + _, err = f.svc.Files.Patch(srcID, &patch).Do() return shouldRetry(err) }) if err != nil { return err } - srcFs.dirCache.ResetRoot() + srcFs.dirCache.FlushDir(srcRemote) return nil } diff --git a/dropbox/dropbox.go b/dropbox/dropbox.go index b4e1a39b1..fd57751a2 100644 --- a/dropbox/dropbox.go +++ b/dropbox/dropbox.go @@ -563,19 +563,22 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) { return dstObj, nil } -// DirMove moves src to this remote using server side move operations. +// DirMove moves src, srcRemote to this remote at dstRemote +// using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists -func (f *Fs) DirMove(src fs.Fs) error { +func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) error { srcFs, ok := src.(*Fs) if !ok { fs.Debugf(srcFs, "Can't move directory - not same remote type") return fs.ErrorCantDirMove } + srcPath := path.Join(srcFs.slashRoot, srcRemote) + dstPath := path.Join(f.slashRoot, dstRemote) // Check if destination exists entry, err := f.db.Metadata(f.slashRoot, false, false, "", "", metadataLimit) @@ -583,8 +586,11 @@ func (f *Fs) DirMove(src fs.Fs) error { return fs.ErrorDirExists } + // Make sure the parent directory exists + // ...apparently not necessary + // Do the move - _, err = f.db.Move(srcFs.slashRoot, f.slashRoot) + _, err = f.db.Move(srcPath, dstPath) if err != nil { return errors.Wrap(err, "MoveDir failed") } diff --git a/fs/fs.go b/fs/fs.go index 9376c054e..923491887 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -256,15 +256,15 @@ type Features struct { // If it isn't possible then return fs.ErrorCantMove Move func(src Object, remote string) (Object, error) - // DirMove moves src to this remote using server side move - // operations. + // DirMove moves src, srcRemote to this remote at dstRemote + // using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists - DirMove func(src Fs) error + DirMove func(src Fs, srcRemote, dstRemote string) error // UnWrap returns the Fs that this Fs is wrapping UnWrap func() Fs @@ -412,15 +412,15 @@ type Mover interface { // DirMover is an optional interface for Fs type DirMover interface { - // DirMove moves src to this remote using server side move - // operations. + // DirMove moves src, srcRemote to this remote at dstRemote + // using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists - DirMove(src Fs) error + DirMove(src Fs, srcRemote, dstRemote string) error } // UnWrapper is an optional interfaces for Fs diff --git a/fs/sync.go b/fs/sync.go index d7f8f3977..fd564c4c4 100644 --- a/fs/sync.go +++ b/fs/sync.go @@ -1107,7 +1107,7 @@ func MoveDir(fdst, fsrc Fs) error { return nil } Debugf(fdst, "Using server side directory move") - err := fdstDirMove(fsrc) + err := fdstDirMove(fsrc, "", "") switch err { case ErrorCantDirMove, ErrorDirExists: Infof(fdst, "Server side directory move failed - fallback to file moves: %v", err) diff --git a/fstest/fstests/fstests.go b/fstest/fstests/fstests.go index c8a64a6ec..58ebdb3e5 100644 --- a/fstest/fstests/fstests.go +++ b/fstest/fstests/fstests.go @@ -481,6 +481,8 @@ func TestFsMove(t *testing.T) { // If destination exists then return fs.ErrorDirExists // TestFsDirMove tests DirMove +// +// go test -v -run '^Test(Setup|Init|FsMkdir|FsPutFile1|FsPutFile2|FsUpdateFile1|FsDirMove)$ func TestFsDirMove(t *testing.T) { skipIfNotOk(t) @@ -491,7 +493,7 @@ func TestFsDirMove(t *testing.T) { } // Check it can't move onto itself - err := doDirMove(remote) + err := doDirMove(remote, "", "") require.Equal(t, fs.ErrorDirExists, err) // new remote @@ -499,17 +501,23 @@ func TestFsDirMove(t *testing.T) { require.NoError(t, err) defer removeNewRemote() + const newName = "new_name/sub_new_name" // try the move - err = newRemote.Features().DirMove(remote) + err = newRemote.Features().DirMove(remote, "", newName) require.NoError(t, err) // check remotes // FIXME: Prints errors. fstest.CheckListing(t, remote, []fstest.Item{}) - fstest.CheckListing(t, newRemote, []fstest.Item{file2, file1}) + file1Copy := file1 + file1Copy.Path = path.Join(newName, file1.Path) + file2Copy := file2 + file2Copy.Path = path.Join(newName, file2.Path) + file2Copy.WinPath = path.Join(newName, file2.WinPath) + fstest.CheckListing(t, newRemote, []fstest.Item{file2Copy, file1Copy}) // move it back - err = doDirMove(newRemote) + err = doDirMove(newRemote, newName, "") require.NoError(t, err) // check remotes diff --git a/local/local.go b/local/local.go index 5a3f31c39..b0b58f310 100644 --- a/local/local.go +++ b/local/local.go @@ -485,38 +485,38 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) { return dstObj, nil } -// DirMove moves src directory to this remote using server side move -// operations. +// DirMove moves src, srcRemote to this remote at dstRemote +// using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists -func (f *Fs) DirMove(src fs.Fs) error { +func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) error { srcFs, ok := src.(*Fs) if !ok { fs.Debugf(srcFs, "Can't move directory - not same remote type") return fs.ErrorCantDirMove } - // Check if source exists - sstat, err := os.Lstat(srcFs.root) - if err != nil { - return err - } - // And is a directory - if !sstat.IsDir() { - return fs.ErrorCantDirMove - } + srcPath := f.cleanPath(filepath.Join(srcFs.root, srcRemote)) + dstPath := f.cleanPath(filepath.Join(f.root, dstRemote)) // Check if destination exists - _, err = os.Lstat(f.root) + _, err := os.Lstat(dstPath) if !os.IsNotExist(err) { return fs.ErrorDirExists } + // Create parent of destination + dstParentPath, _ := getDirFile(dstPath) + err = os.MkdirAll(dstParentPath, 0777) + if err != nil { + return err + } + // Do the move - return os.Rename(srcFs.root, f.root) + return os.Rename(srcPath, dstPath) } // Hashes returns the supported hash sets. diff --git a/sftp/sftp.go b/sftp/sftp.go index 411c97b57..88a439773 100644 --- a/sftp/sftp.go +++ b/sftp/sftp.go @@ -384,23 +384,25 @@ func (f *Fs) Move(src fs.Object, remote string) (fs.Object, error) { return dstObj, nil } -// DirMove moves src directory to this remote using server side move -// operations. +// DirMove moves src, srcRemote to this remote at dstRemote +// using server side move operations. // // Will only be called if src.Fs().Name() == f.Name() // // If it isn't possible then return fs.ErrorCantDirMove // // If destination exists then return fs.ErrorDirExists -func (f *Fs) DirMove(src fs.Fs) error { +func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) error { srcFs, ok := src.(*Fs) if !ok { fs.Debugf(srcFs, "Can't move directory - not same remote type") return fs.ErrorCantDirMove } + srcPath := path.Join(srcFs.root, srcRemote) + dstPath := path.Join(f.root, dstRemote) // Check if destination exists - ok, err := f.dirExists(f.root) + ok, err := f.dirExists(dstPath) if err != nil { return errors.Wrap(err, "DirMove dirExists dst failed") } @@ -408,31 +410,19 @@ func (f *Fs) DirMove(src fs.Fs) error { return fs.ErrorDirExists } - // Refuse to move to or from the root - if f.root == "" || srcFs.root == "" { - fs.Debugf(src, "DirMove error: Can't move root") - return errors.New("can't move root directory") - } - // Make sure the parent directory exists - // err = f.mkParentDir(f.root) - // if err != nil { - // return errors.Wrap(err, "DirMove mkParentDir dst failed") - // } - - // Make sure the source directory exists - err = srcFs.mkdir(srcFs.root) + err = f.mkdir(path.Dir(dstPath)) if err != nil { - return errors.Wrap(err, "DirMove mkdir src failed") + return errors.Wrap(err, "DirMove mkParentDir dst failed") } // Do the move err = f.sftpClient.Rename( - srcFs.root, - f.root, + srcPath, + dstPath, ) if err != nil { - return errors.Wrapf(err, "DirMove Rename(%q,%q) failed", srcFs.root, f.root) + return errors.Wrapf(err, "DirMove Rename(%q,%q) failed", srcPath, dstPath) } return nil }