Add directory parameter to Rmdir and Mkdir #100 #831

This will enable rclone to manage directories properly in the future.
This commit is contained in:
Nick Craig-Wood 2016-11-25 21:52:43 +00:00
parent c41b67ea08
commit aaa1370a36
34 changed files with 220 additions and 89 deletions

View File

@ -18,6 +18,7 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
"path"
"regexp" "regexp"
"strings" "strings"
"sync/atomic" "sync/atomic"
@ -607,8 +608,15 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the container if it doesn't exist // Mkdir creates the container if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
return f.dirCache.FindRoot(true) err := f.dirCache.FindRoot(true)
if err != nil {
return err
}
if dir != "" {
_, err = f.dirCache.FindDir(dir, true)
}
return err
} }
// Move src to this remote using server side move operations. // Move src to this remote using server side move operations.
@ -685,16 +693,16 @@ func (f *Fs) DirMove(src fs.Fs) (err error) {
// purgeCheck remotes the root directory, if check is set then it // purgeCheck remotes the root directory, if check is set then it
// refuses to do so if it has anything in // refuses to do so if it has anything in
func (f *Fs) purgeCheck(check bool) error { func (f *Fs) purgeCheck(dir string, check bool) error {
if f.root == "" { root := path.Join(f.root, dir)
if root == "" {
return errors.New("can't purge root directory") return errors.New("can't purge root directory")
} }
dc := f.dirCache dc := f.dirCache
err := dc.FindRoot(false) rootID, err := dc.FindDir(dir, false)
if err != nil { if err != nil {
return err return err
} }
rootID := dc.RootID()
if check { if check {
// check directory is empty // check directory is empty
@ -730,7 +738,7 @@ func (f *Fs) purgeCheck(check bool) error {
return err return err
} }
f.dirCache.ResetRoot() f.dirCache.FlushDir(dir)
if err != nil { if err != nil {
return err return err
} }
@ -740,8 +748,8 @@ func (f *Fs) purgeCheck(check bool) error {
// Rmdir deletes the root folder // Rmdir deletes the root folder
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
return f.purgeCheck(true) return f.purgeCheck(dir, true)
} }
// Precision return the precision of this Fs // Precision return the precision of this Fs
@ -783,7 +791,7 @@ func (f *Fs) Hashes() fs.HashSet {
// deleting all the files quicker than just running Remove() on the // deleting all the files quicker than just running Remove() on the
// result of List() // result of List()
func (f *Fs) Purge() error { func (f *Fs) Purge() error {
return f.purgeCheck(false) return f.purgeCheck("", false)
} }
// ------------------------------------------------------------ // ------------------------------------------------------------

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -745,7 +745,11 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the bucket if it doesn't exist // Mkdir creates the bucket if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
// Can't create subdirs
if dir != "" {
return nil
}
opts := rest.Opts{ opts := rest.Opts{
Method: "POST", Method: "POST",
Path: "/b2_create_bucket", Path: "/b2_create_bucket",
@ -784,8 +788,8 @@ func (f *Fs) Mkdir() error {
// Rmdir deletes the bucket if the fs is at the root // Rmdir deletes the bucket if the fs is at the root
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
if f.root != "" { if f.root != "" || dir != "" {
return nil return nil
} }
opts := rest.Opts{ opts := rest.Opts{
@ -896,7 +900,7 @@ func (f *Fs) purge(oldOnly bool) error {
wg.Wait() wg.Wait()
if !oldOnly { if !oldOnly {
checkErr(f.Rmdir()) checkErr(f.Rmdir(""))
} }
return errReturn return errReturn
} }

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -17,7 +17,7 @@ var mkdirCmd = &cobra.Command{
cmd.CheckArgs(1, 1, command, args) cmd.CheckArgs(1, 1, command, args)
fdst := cmd.NewFsDst(args) fdst := cmd.NewFsDst(args)
cmd.Run(true, command, func() error { cmd.Run(true, command, func() error {
return fs.Mkdir(fdst) return fs.Mkdir(fdst, "")
}) })
}, },
} }

View File

@ -82,7 +82,7 @@ func newRun() *Run {
log.Fatalf("Failed to open remote %q: %v", *RemoteName, err) log.Fatalf("Failed to open remote %q: %v", *RemoteName, err)
} }
err = r.fremote.Mkdir() err = r.fremote.Mkdir("")
if err != nil { if err != nil {
log.Fatalf("Failed to open mkdir %q: %v", *RemoteName, err) log.Fatalf("Failed to open mkdir %q: %v", *RemoteName, err)
} }

View File

@ -20,7 +20,7 @@ objects in it, use purge for that.`,
cmd.CheckArgs(1, 1, command, args) cmd.CheckArgs(1, 1, command, args)
fdst := cmd.NewFsDst(args) fdst := cmd.NewFsDst(args)
cmd.Run(true, command, func() error { cmd.Run(true, command, func() error {
return fs.Rmdir(fdst) return fs.Rmdir(fdst, "")
}) })
}, },
} }

View File

@ -24,6 +24,7 @@ func TestFsString2(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty2(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty2(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound2(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound2(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir2(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir2(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir2(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty2(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty2(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty2(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty2(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound2(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound2(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -24,6 +24,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -78,6 +78,35 @@ func (dc *DirCache) Flush() {
dc.cacheMu.Unlock() dc.cacheMu.Unlock()
} }
// FlushDir flushes the map of all data starting with dir
//
// If dir is empty then this is equivalent to calling ResetRoot
func (dc *DirCache) FlushDir(dir string) {
if dir == "" {
dc.ResetRoot()
return
}
dc.cacheMu.Lock()
// Delete the root dir
ID, ok := dc.cache[dir]
if ok {
delete(dc.cache, dir)
delete(dc.invCache, ID)
}
// And any sub directories
dir += "/"
for key, ID := range dc.cache {
if strings.HasPrefix(key, dir) {
delete(dc.cache, key)
delete(dc.invCache, ID)
}
}
dc.cacheMu.Unlock()
}
// SplitPath splits a path into directory, leaf // SplitPath splits a path into directory, leaf
// //
// Path shouldn't start or end with a / // Path shouldn't start or end with a /

View File

@ -12,6 +12,7 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
"path"
"strings" "strings"
"time" "time"
@ -592,21 +593,30 @@ func (f *Fs) PutUnchecked(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the container if it doesn't exist // Mkdir creates the container if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
return f.dirCache.FindRoot(true) err := f.dirCache.FindRoot(true)
if err != nil {
return err
}
if dir != "" {
_, err = f.dirCache.FindDir(dir, true)
}
return err
} }
// Rmdir deletes the container // Rmdir deletes the container
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
err := f.dirCache.FindRoot(false) root := path.Join(f.root, dir)
dc := f.dirCache
rootID, err := dc.FindDir(dir, false)
if err != nil { if err != nil {
return err return err
} }
var children *drive.ChildList var children *drive.ChildList
err = f.pacer.Call(func() (bool, error) { err = f.pacer.Call(func() (bool, error) {
children, err = f.svc.Children.List(f.dirCache.RootID()).MaxResults(10).Do() children, err = f.svc.Children.List(rootID).MaxResults(10).Do()
return shouldRetry(err) return shouldRetry(err)
}) })
if err != nil { if err != nil {
@ -616,12 +626,12 @@ func (f *Fs) Rmdir() error {
return errors.Errorf("directory not empty: %#v", children.Items) return errors.Errorf("directory not empty: %#v", children.Items)
} }
// Delete the directory if it isn't the root // Delete the directory if it isn't the root
if f.root != "" { if root != "" {
err = f.pacer.Call(func() (bool, error) { err = f.pacer.Call(func() (bool, error) {
if *driveUseTrash { if *driveUseTrash {
_, err = f.svc.Files.Trash(f.dirCache.RootID()).Do() _, err = f.svc.Files.Trash(rootID).Do()
} else { } else {
err = f.svc.Files.Delete(f.dirCache.RootID()).Do() err = f.svc.Files.Delete(rootID).Do()
} }
return shouldRetry(err) return shouldRetry(err)
}) })
@ -629,7 +639,10 @@ func (f *Fs) Rmdir() error {
return err return err
} }
} }
f.dirCache.ResetRoot() f.dirCache.FlushDir(dir)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -448,30 +448,33 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the container if it doesn't exist // Mkdir creates the container if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
entry, err := f.db.Metadata(f.slashRoot, false, false, "", "", metadataLimit) root := path.Join(f.slashRoot, dir)
entry, err := f.db.Metadata(root, false, false, "", "", metadataLimit)
if err == nil { if err == nil {
if entry.IsDir { if entry.IsDir {
return nil return nil
} }
return errors.Errorf("%q already exists as file", f.root) return errors.Errorf("%q already exists as file", f.root)
} }
_, err = f.db.CreateFolder(f.slashRoot) _, err = f.db.CreateFolder(root)
return err return err
} }
// Rmdir deletes the container // Rmdir deletes the container
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
entry, err := f.db.Metadata(f.slashRoot, true, false, "", "", 16) root := path.Join(f.slashRoot, dir)
entry, err := f.db.Metadata(root, true, false, "", "", 16)
if err != nil { if err != nil {
return err return err
} }
if len(entry.Contents) != 0 { if len(entry.Contents) != 0 {
return errors.New("directory not empty") return errors.New("directory not empty")
} }
return f.Purge() _, err = f.db.Delete(root)
return err
} }
// Precision returns the precision // Precision returns the precision

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -135,12 +135,12 @@ type Fs interface {
// Mkdir makes the directory (container, bucket) // Mkdir makes the directory (container, bucket)
// //
// Shouldn't return an error if it already exists // Shouldn't return an error if it already exists
Mkdir() error Mkdir(dir string) error
// Rmdir removes the directory (container, bucket) if empty // Rmdir removes the directory (container, bucket) if empty
// //
// Return an error if it doesn't exist or isn't empty // Return an error if it doesn't exist or isn't empty
Rmdir() error Rmdir(dir string) error
} }
// Info provides an interface to reading information about a filesystem. // Info provides an interface to reading information about a filesystem.

View File

@ -707,12 +707,12 @@ func ListDir(f Fs, w io.Writer) error {
} }
// Mkdir makes a destination directory or container // Mkdir makes a destination directory or container
func Mkdir(f Fs) error { func Mkdir(f Fs, dir string) error {
if Config.DryRun { if Config.DryRun {
Log(f, "Not making directory as dry run is set") Log(f, "Not making directory as dry run is set")
return nil return nil
} }
err := f.Mkdir() err := f.Mkdir(dir)
if err != nil { if err != nil {
Stats.Error() Stats.Error()
return err return err
@ -722,17 +722,17 @@ func Mkdir(f Fs) error {
// TryRmdir removes a container but not if not empty. It doesn't // TryRmdir removes a container but not if not empty. It doesn't
// count errors but may return one. // count errors but may return one.
func TryRmdir(f Fs) error { func TryRmdir(f Fs, dir string) error {
if Config.DryRun { if Config.DryRun {
Log(f, "Not deleting as dry run is set") Log(f, "Not deleting as dry run is set")
return nil return nil
} }
return f.Rmdir() return f.Rmdir(dir)
} }
// Rmdir removes a container but not if not empty // Rmdir removes a container but not if not empty
func Rmdir(f Fs) error { func Rmdir(f Fs, dir string) error {
err := TryRmdir(f) err := TryRmdir(f, dir)
if err != nil { if err != nil {
Stats.Error() Stats.Error()
return err return err
@ -764,7 +764,7 @@ func Purge(f Fs) error {
if err != nil { if err != nil {
return err return err
} }
err = Rmdir(f) err = Rmdir(f, "")
} }
if err != nil { if err != nil {
Stats.Error() Stats.Error()

View File

@ -201,7 +201,7 @@ func (r *Run) WriteObjectTo(f fs.Fs, remote, content string, modTime time.Time,
} }
const maxTries = 10 const maxTries = 10
if !r.mkdir[f.String()] { if !r.mkdir[f.String()] {
err := f.Mkdir() err := f.Mkdir("")
if err != nil { if err != nil {
r.Fatalf("Failed to mkdir %q: %v", f, err) r.Fatalf("Failed to mkdir %q: %v", f, err)
} }

View File

@ -402,7 +402,7 @@ func (s *syncCopyMove) run() error {
return nil return nil
} }
err := Mkdir(s.fdst) err := Mkdir(s.fdst, "")
if err != nil { if err != nil {
return err return err
} }

View File

@ -120,7 +120,7 @@ func TestCopyAfterDelete(t *testing.T) {
fstest.CheckItems(t, r.flocal) fstest.CheckItems(t, r.flocal)
fstest.CheckItems(t, r.fremote, file1) fstest.CheckItems(t, r.fremote, file1)
err := fs.Mkdir(r.flocal) err := fs.Mkdir(r.flocal, "")
require.NoError(t, err) require.NoError(t, err)
err = fs.CopyDir(r.fremote, r.flocal) err = fs.CopyDir(r.fremote, r.flocal)

View File

@ -14,6 +14,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -144,15 +145,16 @@ func (is *Items) Done(t *testing.T) {
// CheckListingWithPrecision checks the fs to see if it has the // CheckListingWithPrecision checks the fs to see if it has the
// expected contents with the given precision. // expected contents with the given precision.
func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, precision time.Duration) { func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, expectedDirs []string, precision time.Duration) {
is := NewItems(items) is := NewItems(items)
oldErrors := fs.Stats.GetErrors() oldErrors := fs.Stats.GetErrors()
var objs []fs.Object var objs []fs.Object
var dirs []*fs.Dir
var err error var err error
var retries = *listRetries var retries = *listRetries
sleep := time.Second / 2 sleep := time.Second / 2
for i := 1; i <= retries; i++ { for i := 1; i <= retries; i++ {
objs, err = fs.NewLister().Start(f, "").GetObjects() objs, dirs, err = fs.NewLister().Start(f, "").GetAll()
if err != nil && err != fs.ErrorDirNotFound { if err != nil && err != fs.ErrorDirNotFound {
t.Fatalf("Error listing: %v", err) t.Fatalf("Error listing: %v", err)
} }
@ -179,18 +181,29 @@ func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, precision ti
if len(items) == 0 && oldErrors == 0 && fs.Stats.GetErrors() == 1 { if len(items) == 0 && oldErrors == 0 && fs.Stats.GetErrors() == 1 {
fs.Stats.ResetErrors() fs.Stats.ResetErrors()
} }
// Check the directories - ignore if no directories returned
// for remotes which can't do directories
if expectedDirs != nil && len(dirs) != 0 {
actualDirs := []string{}
for _, dir := range dirs {
actualDirs = append(actualDirs, dir.Name)
}
sort.Strings(actualDirs)
sort.Strings(expectedDirs)
assert.Equal(t, expectedDirs, actualDirs, "directories")
}
} }
// CheckListing checks the fs to see if it has the expected contents // CheckListing checks the fs to see if it has the expected contents
func CheckListing(t *testing.T, f fs.Fs, items []Item) { func CheckListing(t *testing.T, f fs.Fs, items []Item) {
precision := f.Precision() precision := f.Precision()
CheckListingWithPrecision(t, f, items, precision) CheckListingWithPrecision(t, f, items, nil, precision)
} }
// CheckItems checks the fs to see if it has only the items passed in // CheckItems checks the fs to see if it has only the items passed in
// using a precision of fs.Config.ModifyWindow // using a precision of fs.Config.ModifyWindow
func CheckItems(t *testing.T, f fs.Fs, items ...Item) { func CheckItems(t *testing.T, f fs.Fs, items ...Item) {
CheckListingWithPrecision(t, f, items, fs.Config.ModifyWindow) CheckListingWithPrecision(t, f, items, nil, fs.Config.ModifyWindow)
} }
// Time parses a time string or logs a fatal error // Time parses a time string or logs a fatal error
@ -300,7 +313,7 @@ func RandomRemote(remoteName string, subdir bool) (fs.Fs, string, func(), error)
// TestMkdir tests Mkdir works // TestMkdir tests Mkdir works
func TestMkdir(t *testing.T, remote fs.Fs) { func TestMkdir(t *testing.T, remote fs.Fs) {
err := fs.Mkdir(remote) err := fs.Mkdir(remote, "")
require.NoError(t, err) require.NoError(t, err)
CheckListing(t, remote, []Item{}) CheckListing(t, remote, []Item{})
} }
@ -314,6 +327,6 @@ func TestPurge(t *testing.T, remote fs.Fs) {
// TestRmdir tests Rmdir works // TestRmdir tests Rmdir works
func TestRmdir(t *testing.T, remote fs.Fs) { func TestRmdir(t *testing.T, remote fs.Fs) {
err := fs.Rmdir(remote) err := fs.Rmdir(remote, "")
require.NoError(t, err) require.NoError(t, err)
} }

View File

@ -116,7 +116,7 @@ func TestFsRmdirEmpty(t *testing.T) {
// TestFsRmdirNotFound tests deleting a non existent directory // TestFsRmdirNotFound tests deleting a non existent directory
func TestFsRmdirNotFound(t *testing.T) { func TestFsRmdirNotFound(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
err := remote.Rmdir() err := remote.Rmdir("")
assert.Error(t, err, "Expecting error on Rmdir non existent") assert.Error(t, err, "Expecting error on Rmdir non existent")
} }
@ -127,6 +127,23 @@ func TestFsMkdir(t *testing.T) {
fstest.TestMkdir(t, remote) fstest.TestMkdir(t, remote)
} }
// TestFsMkdirRmdirSubdir tests making and removing a sub directory
func TestFsMkdirRmdirSubdir(t *testing.T) {
skipIfNotOk(t)
dir := "dir/subdir"
err := fs.Mkdir(remote, dir)
require.NoError(t, err)
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir", "dir/subdir"}, fs.Config.ModifyWindow)
err = fs.Rmdir(remote, dir)
require.NoError(t, err)
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{"dir"}, fs.Config.ModifyWindow)
err = fs.Rmdir(remote, "dir")
require.NoError(t, err)
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{}, []string{}, fs.Config.ModifyWindow)
}
// TestFsListEmpty tests listing an empty directory // TestFsListEmpty tests listing an empty directory
func TestFsListEmpty(t *testing.T) { func TestFsListEmpty(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
@ -501,7 +518,7 @@ func TestFsDirMove(t *testing.T) {
// TestFsRmdirFull tests removing a non empty directory // TestFsRmdirFull tests removing a non empty directory
func TestFsRmdirFull(t *testing.T) { func TestFsRmdirFull(t *testing.T) {
skipIfNotOk(t) skipIfNotOk(t)
err := remote.Rmdir() err := remote.Rmdir("")
require.Error(t, err, "Expecting error on RMdir on non empty remote") require.Error(t, err, "Expecting error on RMdir on non empty remote")
} }

View File

@ -448,7 +448,11 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the bucket if it doesn't exist // Mkdir creates the bucket if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
// Can't create subdirs
if dir != "" {
return nil
}
_, err := f.svc.Buckets.Get(f.bucket).Do() _, err := f.svc.Buckets.Get(f.bucket).Do()
if err == nil { if err == nil {
// Bucket already exists // Bucket already exists
@ -470,8 +474,8 @@ func (f *Fs) Mkdir() error {
// //
// Returns an error if it isn't empty: Error 409: The bucket you tried // Returns an error if it isn't empty: Error 409: The bucket you tried
// to delete was not empty. // to delete was not empty.
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
if f.root != "" { if f.root != "" || dir != "" {
return nil return nil
} }
return f.svc.Buckets.Delete(f.bucket).Do() return f.svc.Buckets.Delete(f.bucket).Do()

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -296,25 +296,28 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the directory if it doesn't exist // Mkdir creates the directory if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
// FIXME: https://github.com/syncthing/syncthing/blob/master/lib/osutil/mkdirall_windows.go // FIXME: https://github.com/syncthing/syncthing/blob/master/lib/osutil/mkdirall_windows.go
err := os.MkdirAll(f.root, 0777) root := path.Join(f.root, dir)
err := os.MkdirAll(root, 0777)
if err != nil { if err != nil {
return err return err
} }
fi, err := os.Lstat(f.root) if dir == "" {
if err != nil { fi, err := os.Lstat(root)
return err if err != nil {
return err
}
f.dev = readDevice(fi)
} }
f.dev = readDevice(fi)
return nil return nil
} }
// Rmdir removes the directory // Rmdir removes the directory
// //
// If it isn't empty it will return an error // If it isn't empty it will return an error
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
return os.Remove(f.root) return os.Remove(path.Join(f.root, dir))
} }
// Precision of the file system // Precision of the file system

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -9,6 +9,7 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"path"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@ -452,8 +453,15 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the container if it doesn't exist // Mkdir creates the container if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
return f.dirCache.FindRoot(true) err := f.dirCache.FindRoot(true)
if err != nil {
return err
}
if dir != "" {
_, err = f.dirCache.FindDir(dir, true)
}
return err
} }
// deleteObject removes an object by ID // deleteObject removes an object by ID
@ -471,17 +479,17 @@ func (f *Fs) deleteObject(id string) error {
// purgeCheck removes the root directory, if check is set then it // purgeCheck removes the root directory, if check is set then it
// refuses to do so if it has anything in // refuses to do so if it has anything in
func (f *Fs) purgeCheck(check bool) error { func (f *Fs) purgeCheck(dir string, check bool) error {
if f.root == "" { root := path.Join(f.root, dir)
if root == "" {
return errors.New("can't purge root directory") return errors.New("can't purge root directory")
} }
dc := f.dirCache dc := f.dirCache
err := dc.FindRoot(false) rootID, err := dc.FindDir(dir, false)
if err != nil { if err != nil {
return err return err
} }
rootID := dc.RootID() item, _, err := f.readMetaDataForPath(root)
item, _, err := f.readMetaDataForPath(f.root)
if err != nil { if err != nil {
return err return err
} }
@ -495,7 +503,7 @@ func (f *Fs) purgeCheck(check bool) error {
if err != nil { if err != nil {
return err return err
} }
f.dirCache.ResetRoot() f.dirCache.FlushDir(dir)
if err != nil { if err != nil {
return err return err
} }
@ -505,8 +513,8 @@ func (f *Fs) purgeCheck(check bool) error {
// Rmdir deletes the root folder // Rmdir deletes the root folder
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
return f.purgeCheck(true) return f.purgeCheck(dir, true)
} }
// Precision return the precision of this Fs // Precision return the precision of this Fs
@ -624,7 +632,7 @@ func (f *Fs) Copy(src fs.Object, remote string) (fs.Object, error) {
// deleting all the files quicker than just running Remove() on the // deleting all the files quicker than just running Remove() on the
// result of List() // result of List()
func (f *Fs) Purge() error { func (f *Fs) Purge() error {
return f.purgeCheck(false) return f.purgeCheck("", false)
} }
// Hashes returns the supported hash sets. // Hashes returns the supported hash sets.

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -627,7 +627,11 @@ func (f *Fs) dirExists() (bool, error) {
} }
// Mkdir creates the bucket if it doesn't exist // Mkdir creates the bucket if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
// Can't create subdirs
if dir != "" {
return nil
}
exists, err := f.dirExists() exists, err := f.dirExists()
if err != nil || exists { if err != nil || exists {
return err return err
@ -653,8 +657,8 @@ func (f *Fs) Mkdir() error {
// Rmdir deletes the bucket if the fs is at the root // Rmdir deletes the bucket if the fs is at the root
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
if f.root != "" { if f.root != "" || dir != "" {
return nil return nil
} }
req := s3.DeleteBucketInput{ req := s3.DeleteBucketInput{

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -403,7 +403,11 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the container if it doesn't exist // Mkdir creates the container if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
// Can't create subdirs
if dir != "" {
return nil
}
// Check to see if container exists first // Check to see if container exists first
_, _, err := f.c.Container(f.container) _, _, err := f.c.Container(f.container)
if err == nil { if err == nil {
@ -419,8 +423,8 @@ func (f *Fs) Mkdir() error {
// Rmdir deletes the container if the fs is at the root // Rmdir deletes the container if the fs is at the root
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
if f.root != "" { if f.root != "" || dir != "" {
return nil return nil
} }
return f.c.ContainerDelete(f.container) return f.c.ContainerDelete(f.container)
@ -459,7 +463,7 @@ func (f *Fs) Purge() error {
if err != nil { if err != nil {
return err return err
} }
return f.Rmdir() return f.Rmdir("")
} }
// Copy src to this remote using server side copy operations. // Copy src to this remote using server side copy operations.

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }

View File

@ -390,25 +390,33 @@ func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) {
} }
// Mkdir creates the container if it doesn't exist // Mkdir creates the container if it doesn't exist
func (f *Fs) Mkdir() error { func (f *Fs) Mkdir(dir string) error {
return mkDirFullPath(f.yd, f.diskRoot) root := f.diskRoot
if dir != "" {
root += dir + "/"
}
return mkDirFullPath(f.yd, root)
} }
// Rmdir deletes the container // Rmdir deletes the container
// //
// Returns an error if it isn't empty // Returns an error if it isn't empty
func (f *Fs) Rmdir() error { func (f *Fs) Rmdir(dir string) error {
return f.purgeCheck(true) return f.purgeCheck(dir, true)
} }
// purgeCheck remotes the root directory, if check is set then it // purgeCheck remotes the root directory, if check is set then it
// refuses to do so if it has anything in // refuses to do so if it has anything in
func (f *Fs) purgeCheck(check bool) error { func (f *Fs) purgeCheck(dir string, check bool) error {
root := f.diskRoot
if dir != "" {
root += dir + "/"
}
if check { if check {
//to comply with rclone logic we check if the directory is empty before delete. //to comply with rclone logic we check if the directory is empty before delete.
//send request to get list of objects in this directory. //send request to get list of objects in this directory.
var opt yandex.ResourceInfoRequestOptions var opt yandex.ResourceInfoRequestOptions
ResourceInfoResponse, err := f.yd.NewResourceInfoRequest(f.diskRoot, opt).Exec() ResourceInfoResponse, err := f.yd.NewResourceInfoRequest(root, opt).Exec()
if err != nil { if err != nil {
return errors.Wrap(err, "rmdir failed") return errors.Wrap(err, "rmdir failed")
} }
@ -417,7 +425,7 @@ func (f *Fs) purgeCheck(check bool) error {
} }
} }
//delete directory //delete directory
return f.yd.Delete(f.diskRoot, true) return f.yd.Delete(root, true)
} }
// Precision return the precision of this Fs // Precision return the precision of this Fs
@ -431,7 +439,7 @@ func (f *Fs) Precision() time.Duration {
// deleting all the files quicker than just running Remove() on the // deleting all the files quicker than just running Remove() on the
// result of List() // result of List()
func (f *Fs) Purge() error { func (f *Fs) Purge() error {
return f.purgeCheck(false) return f.purgeCheck("", false)
} }
// Hashes returns the supported hash sets. // Hashes returns the supported hash sets.

View File

@ -23,6 +23,7 @@ func TestFsString(t *testing.T) { fstests.TestFsString(t) }
func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) } func TestFsRmdirEmpty(t *testing.T) { fstests.TestFsRmdirEmpty(t) }
func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) } func TestFsRmdirNotFound(t *testing.T) { fstests.TestFsRmdirNotFound(t) }
func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) } func TestFsMkdir(t *testing.T) { fstests.TestFsMkdir(t) }
func TestFsMkdirRmdirSubdir(t *testing.T) { fstests.TestFsMkdirRmdirSubdir(t) }
func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) } func TestFsListEmpty(t *testing.T) { fstests.TestFsListEmpty(t) }
func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) } func TestFsListDirEmpty(t *testing.T) { fstests.TestFsListDirEmpty(t) }
func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) } func TestFsNewObjectNotFound(t *testing.T) { fstests.TestFsNewObjectNotFound(t) }