Skip to content

Commit

Permalink
Add srcRemote and dstRemote parameters to DirMove #954
Browse files Browse the repository at this point in the history
  • Loading branch information
ncw committed Feb 16, 2017
1 parent e4835f5 commit f3c5745
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 89 deletions.
84 changes: 61 additions & 23 deletions amazonclouddrive/amazonclouddrive.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
// find the root src directory
err = srcFs.dirCache.FindRoot(false)
if err != nil {
return err
}

// Find the ID of the source and make a node from it
err = srcFs.dirCache.FindRoot(false)
// 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 {
fs.Debugf(src, "DirMove error: error finding src root: %v", err)
return err
}
srcDirectoryID, err := srcFs.dirCache.RootParentID()

// 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()
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions crypt/crypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
64 changes: 51 additions & 13 deletions drive/drive.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}

Expand Down
12 changes: 9 additions & 3 deletions dropbox/dropbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,28 +563,34 @@ 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)
if err == nil && !entry.IsDeleted {
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")
}
Expand Down
12 changes: 6 additions & 6 deletions fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion fs/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 12 additions & 4 deletions fstest/fstests/fstests.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -491,25 +493,31 @@ 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
newRemote, _, removeNewRemote, err := fstest.RandomRemote(RemoteName, false)
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
Expand Down
Loading

0 comments on commit f3c5745

Please sign in to comment.