mirror of
https://github.com/rclone/rclone.git
synced 2025-01-07 06:50:24 +01:00
fs: Add directory to optional Purge interface - fixes #1891
- add a directory to the optional Purge interface - fix up all the backends - add an additional integration test to test for the feature - use the new feature in operations.Purge Many of the backends had been prepared in advance for this so the change was trivial for them.
This commit is contained in:
parent
c2f3949ded
commit
a2afa9aadd
@ -937,8 +937,8 @@ func (f *Fs) Hashes() hash.Set {
|
|||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
@ -967,8 +967,7 @@ func (f *Fs) Hashes() hash.Set {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and directories including the old versions.
|
// Purge deletes all the files and directories including the old versions.
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
dir := "" // forward compat!
|
|
||||||
container, directory := f.split(dir)
|
container, directory := f.split(dir)
|
||||||
if container == "" || directory != "" {
|
if container == "" || directory != "" {
|
||||||
// Delegate to caller if not root of a container
|
// Delegate to caller if not root of a container
|
||||||
|
@ -1143,7 +1143,8 @@ func (f *Fs) deleteByID(ctx context.Context, ID, Name string) error {
|
|||||||
// if oldOnly is true then it deletes only non current files.
|
// if oldOnly is true then it deletes only non current files.
|
||||||
//
|
//
|
||||||
// Implemented here so we can make sure we delete old versions.
|
// Implemented here so we can make sure we delete old versions.
|
||||||
func (f *Fs) purge(ctx context.Context, bucket, directory string, oldOnly bool) error {
|
func (f *Fs) purge(ctx context.Context, dir string, oldOnly bool) error {
|
||||||
|
bucket, directory := f.split(dir)
|
||||||
if bucket == "" {
|
if bucket == "" {
|
||||||
return errors.New("can't purge from root")
|
return errors.New("can't purge from root")
|
||||||
}
|
}
|
||||||
@ -1218,19 +1219,19 @@ func (f *Fs) purge(ctx context.Context, bucket, directory string, oldOnly bool)
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
if !oldOnly {
|
if !oldOnly {
|
||||||
checkErr(f.Rmdir(ctx, ""))
|
checkErr(f.Rmdir(ctx, dir))
|
||||||
}
|
}
|
||||||
return errReturn
|
return errReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and directories including the old versions.
|
// Purge deletes all the files and directories including the old versions.
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purge(ctx, f.rootBucket, f.rootDirectory, false)
|
return f.purge(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp deletes all the hidden files.
|
// CleanUp deletes all the hidden files.
|
||||||
func (f *Fs) CleanUp(ctx context.Context) error {
|
func (f *Fs) CleanUp(ctx context.Context) error {
|
||||||
return f.purge(ctx, f.rootBucket, f.rootDirectory, true)
|
return f.purge(ctx, "", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy does a server side copy from dstObj <- srcObj
|
// copy does a server side copy from dstObj <- srcObj
|
||||||
|
@ -862,8 +862,8 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// move a file or folder
|
// move a file or folder
|
||||||
|
11
backend/cache/cache.go
vendored
11
backend/cache/cache.go
vendored
@ -1702,17 +1702,20 @@ func (f *Fs) Hashes() hash.Set {
|
|||||||
return f.Fs.Hashes()
|
return f.Fs.Hashes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge all files in the root and the root directory
|
// Purge all files in the directory
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
|
if dir == "" {
|
||||||
|
// FIXME this isn't quite right as it should purge the dir prefix
|
||||||
fs.Infof(f, "purging cache")
|
fs.Infof(f, "purging cache")
|
||||||
f.cache.Purge()
|
f.cache.Purge()
|
||||||
|
}
|
||||||
|
|
||||||
do := f.Fs.Features().Purge
|
do := f.Fs.Features().Purge
|
||||||
if do == nil {
|
if do == nil {
|
||||||
return nil
|
return fs.ErrorCantPurge
|
||||||
}
|
}
|
||||||
|
|
||||||
err := do(ctx)
|
err := do(ctx, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
4
backend/cache/cache_internal_test.go
vendored
4
backend/cache/cache_internal_test.go
vendored
@ -946,7 +946,7 @@ func (r *run) newCacheFs(t *testing.T, remote, id string, needRemote, purge bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
if purge {
|
if purge {
|
||||||
_ = f.Features().Purge(context.Background())
|
_ = f.Features().Purge(context.Background(), "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
err = f.Mkdir(context.Background(), "")
|
err = f.Mkdir(context.Background(), "")
|
||||||
@ -955,7 +955,7 @@ func (r *run) newCacheFs(t *testing.T, remote, id string, needRemote, purge bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *run) cleanupFs(t *testing.T, f fs.Fs, b *cache.Persistent) {
|
func (r *run) cleanupFs(t *testing.T, f fs.Fs, b *cache.Persistent) {
|
||||||
err := f.Features().Purge(context.Background())
|
err := f.Features().Purge(context.Background(), "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
cfs, err := r.getCacheFs(f)
|
cfs, err := r.getCacheFs(f)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -1333,7 +1333,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return f.base.Rmdir(ctx, dir)
|
return f.base.Rmdir(ctx, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge all files in the root and the root directory
|
// Purge all files in the directory
|
||||||
//
|
//
|
||||||
// Implement this if you have a way of deleting all the files
|
// Implement this if you have a way of deleting all the files
|
||||||
// quicker than just running Remove() on the result of List()
|
// quicker than just running Remove() on the result of List()
|
||||||
@ -1344,12 +1344,12 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
// As a result it removes not only composite chunker files with their
|
// As a result it removes not only composite chunker files with their
|
||||||
// active chunks but also all hidden temporary chunks in the directory.
|
// active chunks but also all hidden temporary chunks in the directory.
|
||||||
//
|
//
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
do := f.base.Features().Purge
|
do := f.base.Features().Purge
|
||||||
if do == nil {
|
if do == nil {
|
||||||
return fs.ErrorCantPurge
|
return fs.ErrorCantPurge
|
||||||
}
|
}
|
||||||
return do(ctx)
|
return do(ctx, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove an object (chunks and metadata, if any)
|
// Remove an object (chunks and metadata, if any)
|
||||||
|
@ -427,18 +427,18 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return f.Fs.Rmdir(ctx, f.cipher.EncryptDirName(dir))
|
return f.Fs.Rmdir(ctx, f.cipher.EncryptDirName(dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge all files in the root and the root directory
|
// Purge all files in the directory specified
|
||||||
//
|
//
|
||||||
// Implement this if you have a way of deleting all the files
|
// Implement this if you have a way of deleting all the files
|
||||||
// quicker than just running Remove() on the result of List()
|
// quicker than just running Remove() on the result of List()
|
||||||
//
|
//
|
||||||
// Return an error if it doesn't exist
|
// Return an error if it doesn't exist
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
do := f.Fs.Features().Purge
|
do := f.Fs.Features().Purge
|
||||||
if do == nil {
|
if do == nil {
|
||||||
return fs.ErrorCantPurge
|
return fs.ErrorCantPurge
|
||||||
}
|
}
|
||||||
return do(ctx)
|
return do(ctx, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy src to this remote using server side copy operations.
|
// Copy src to this remote using server side copy operations.
|
||||||
|
@ -2208,10 +2208,9 @@ func (f *Fs) delete(ctx context.Context, id string, useTrash bool) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rmdir deletes a directory
|
// purgeCheck removes the dir directory, if check is set then it
|
||||||
//
|
// refuses to do so if it has anything in
|
||||||
// Returns an error if it isn't empty
|
func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
|
||||||
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|
||||||
root := path.Join(f.root, dir)
|
root := path.Join(f.root, dir)
|
||||||
dc := f.dirCache
|
dc := f.dirCache
|
||||||
directoryID, err := dc.FindDir(ctx, dir, false)
|
directoryID, err := dc.FindDir(ctx, dir, false)
|
||||||
@ -2224,6 +2223,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return f.delete(ctx, shortcutID, f.opt.UseTrash)
|
return f.delete(ctx, shortcutID, f.opt.UseTrash)
|
||||||
}
|
}
|
||||||
var trashedFiles = false
|
var trashedFiles = false
|
||||||
|
if check {
|
||||||
found, err := f.list(ctx, []string{directoryID}, "", false, false, true, func(item *drive.File) bool {
|
found, err := f.list(ctx, []string{directoryID}, "", false, false, true, func(item *drive.File) bool {
|
||||||
if !item.Trashed {
|
if !item.Trashed {
|
||||||
fs.Debugf(dir, "Rmdir: contains file: %q", item.Name)
|
fs.Debugf(dir, "Rmdir: contains file: %q", item.Name)
|
||||||
@ -2239,6 +2239,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
if found {
|
if found {
|
||||||
return errors.Errorf("directory not empty")
|
return errors.Errorf("directory not empty")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if root != "" {
|
if root != "" {
|
||||||
// trash the directory if it had trashed files
|
// trash the directory if it had trashed files
|
||||||
// in or the user wants to trash, otherwise
|
// in or the user wants to trash, otherwise
|
||||||
@ -2247,6 +2248,8 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else if check {
|
||||||
|
return errors.New("can't purge root directory")
|
||||||
}
|
}
|
||||||
f.dirCache.FlushDir(dir)
|
f.dirCache.FlushDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2255,6 +2258,13 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rmdir deletes a directory
|
||||||
|
//
|
||||||
|
// Returns an error if it isn't empty
|
||||||
|
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
||||||
|
return f.purgeCheck(ctx, dir, true)
|
||||||
|
}
|
||||||
|
|
||||||
// Precision of the object storage system
|
// Precision of the object storage system
|
||||||
func (f *Fs) Precision() time.Duration {
|
func (f *Fs) Precision() time.Duration {
|
||||||
return time.Millisecond
|
return time.Millisecond
|
||||||
@ -2348,23 +2358,11 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
if f.root == "" {
|
|
||||||
return errors.New("can't purge root directory")
|
|
||||||
}
|
|
||||||
if f.opt.TrashedOnly {
|
if f.opt.TrashedOnly {
|
||||||
return errors.New("Can't purge with --drive-trashed-only. Use delete if you want to selectively delete files")
|
return errors.New("Can't purge with --drive-trashed-only. Use delete if you want to selectively delete files")
|
||||||
}
|
}
|
||||||
rootID, err := f.dirCache.RootID(ctx, false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = f.delete(ctx, shortcutID(rootID), f.opt.UseTrash)
|
|
||||||
f.dirCache.ResetRoot()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp empties the trash
|
// CleanUp empties the trash
|
||||||
|
@ -611,10 +611,9 @@ func (f *Fs) Mkdir(ctx context.Context, dir string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rmdir deletes the container
|
// purgeCheck removes the root directory, if check is set then it
|
||||||
//
|
// refuses to do so if it has anything in
|
||||||
// Returns an error if it isn't empty
|
func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) (err error) {
|
||||||
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|
||||||
root := path.Join(f.slashRoot, dir)
|
root := path.Join(f.slashRoot, dir)
|
||||||
|
|
||||||
// can't remove root
|
// can't remove root
|
||||||
@ -622,8 +621,9 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return errors.New("can't remove root directory")
|
return errors.New("can't remove root directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if check {
|
||||||
// check directory exists
|
// check directory exists
|
||||||
_, err := f.getDirMetadata(root)
|
_, err = f.getDirMetadata(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Rmdir")
|
return errors.Wrap(err, "Rmdir")
|
||||||
}
|
}
|
||||||
@ -648,6 +648,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
if len(res.Entries) != 0 {
|
if len(res.Entries) != 0 {
|
||||||
return errors.New("directory not empty")
|
return errors.New("directory not empty")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove it
|
// remove it
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
@ -657,6 +658,13 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rmdir deletes the container
|
||||||
|
//
|
||||||
|
// Returns an error if it isn't empty
|
||||||
|
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
||||||
|
return f.purgeCheck(ctx, dir, true)
|
||||||
|
}
|
||||||
|
|
||||||
// Precision returns the precision
|
// Precision returns the precision
|
||||||
func (f *Fs) Precision() time.Duration {
|
func (f *Fs) Precision() time.Duration {
|
||||||
return time.Second
|
return time.Second
|
||||||
@ -719,15 +727,8 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) (err error) {
|
func (f *Fs) Purge(ctx context.Context, dir string) (err error) {
|
||||||
// Let dropbox delete the filesystem tree
|
return f.purgeCheck(ctx, dir, false)
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
|
||||||
_, err = f.srv.DeleteV2(&files.DeleteArg{
|
|
||||||
Path: f.opt.Enc.FromStandardPath(f.slashRoot),
|
|
||||||
})
|
|
||||||
return shouldRetry(err)
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move src to this remote using server side move operations.
|
// Move src to this remote using server side move operations.
|
||||||
|
@ -1070,8 +1070,8 @@ func (f *Fs) Precision() time.Duration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files and the container
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyOrMoves copies or moves directories or files depending on the method parameter
|
// copyOrMoves copies or moves directories or files depending on the method parameter
|
||||||
|
@ -616,20 +616,21 @@ func (f *Fs) readPrecision() (precision time.Duration) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and directories
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
fi, err := f.lstat(f.root)
|
dir = f.localPath(dir)
|
||||||
|
fi, err := f.lstat(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !fi.Mode().IsDir() {
|
if !fi.Mode().IsDir() {
|
||||||
return errors.Errorf("can't purge non directory: %q", f.root)
|
return errors.Errorf("can't purge non directory: %q", dir)
|
||||||
}
|
}
|
||||||
return os.RemoveAll(f.root)
|
return os.RemoveAll(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move src to this remote using server side move operations.
|
// Move src to this remote using server side move operations.
|
||||||
|
@ -1162,12 +1162,12 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return f.purgeWithCheck(ctx, dir, true, "rmdir")
|
return f.purgeWithCheck(ctx, dir, true, "rmdir")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the root directory
|
// Purge deletes all the files in the directory
|
||||||
// Optional interface: Only implement this if you have a way of deleting
|
// Optional interface: Only implement this if you have a way of deleting
|
||||||
// all the files quicker than just running Remove() on the result of List()
|
// all the files quicker than just running Remove() on the result of List()
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
// fs.Debugf(f, ">>> Purge")
|
// fs.Debugf(f, ">>> Purge")
|
||||||
return f.purgeWithCheck(ctx, "", false, "purge")
|
return f.purgeWithCheck(ctx, dir, false, "purge")
|
||||||
}
|
}
|
||||||
|
|
||||||
// purgeWithCheck() removes the root directory.
|
// purgeWithCheck() removes the root directory.
|
||||||
|
@ -669,13 +669,13 @@ func (f *Fs) Precision() time.Duration {
|
|||||||
return fs.ModTimeNotSupported
|
return fs.ModTimeNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck("", false)
|
return f.purgeCheck(dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// move a file or folder (srcFs, srcRemote, info) to (f, dstRemote)
|
// move a file or folder (srcFs, srcRemote, info) to (f, dstRemote)
|
||||||
|
@ -1073,13 +1073,13 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
return dstObj, nil
|
return dstObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move src to this remote using server side move operations.
|
// Move src to this remote using server side move operations.
|
||||||
|
@ -506,13 +506,13 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an Object from a path
|
// Return an Object from a path
|
||||||
|
@ -671,13 +671,13 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
return dstObj, nil
|
return dstObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp empties the trash
|
// CleanUp empties the trash
|
||||||
|
@ -609,13 +609,13 @@ func (f *Fs) Precision() time.Duration {
|
|||||||
return fs.ModTimeNotSupported
|
return fs.ModTimeNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// move a file or folder
|
// move a file or folder
|
||||||
|
@ -458,10 +458,9 @@ func (f *Fs) Mkdir(ctx context.Context, dir string) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rmdir deletes the container
|
// purgeCheck removes the root directory, if check is set then it
|
||||||
//
|
// refuses to do so if it has anything in
|
||||||
// Returns an error if it isn't empty
|
func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) (err error) {
|
||||||
func (f *Fs) Rmdir(ctx context.Context, dir string) (err error) {
|
|
||||||
// defer log.Trace(f, "dir=%v", dir)("err=%v", &err)
|
// defer log.Trace(f, "dir=%v", dir)("err=%v", &err)
|
||||||
|
|
||||||
root := strings.Trim(path.Join(f.root, dir), "/")
|
root := strings.Trim(path.Join(f.root, dir), "/")
|
||||||
@ -478,6 +477,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) (err error) {
|
|||||||
}
|
}
|
||||||
dirID := atoi(directoryID)
|
dirID := atoi(directoryID)
|
||||||
|
|
||||||
|
if check {
|
||||||
// check directory empty
|
// check directory empty
|
||||||
var children []putio.File
|
var children []putio.File
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
@ -491,6 +491,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) (err error) {
|
|||||||
if len(children) != 0 {
|
if len(children) != 0 {
|
||||||
return errors.New("directory not empty")
|
return errors.New("directory not empty")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove it
|
// remove it
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
@ -502,35 +503,26 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rmdir deletes the container
|
||||||
|
//
|
||||||
|
// Returns an error if it isn't empty
|
||||||
|
func (f *Fs) Rmdir(ctx context.Context, dir string) (err error) {
|
||||||
|
return f.purgeCheck(ctx, dir, true)
|
||||||
|
}
|
||||||
|
|
||||||
// Precision returns the precision
|
// Precision returns the precision
|
||||||
func (f *Fs) Precision() time.Duration {
|
func (f *Fs) Precision() time.Duration {
|
||||||
return time.Second
|
return time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) (err error) {
|
func (f *Fs) Purge(ctx context.Context, dir string) (err error) {
|
||||||
// defer log.Trace(f, "")("err=%v", &err)
|
// defer log.Trace(f, "")("err=%v", &err)
|
||||||
|
return f.purgeCheck(ctx, dir, false)
|
||||||
if f.root == "" {
|
|
||||||
return errors.New("can't purge root directory")
|
|
||||||
}
|
|
||||||
rootIDs, err := f.dirCache.RootID(ctx, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rootID := atoi(rootIDs)
|
|
||||||
// Let putio delete the filesystem tree
|
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
|
||||||
// fs.Debugf(f, "deleting file: %d", rootID)
|
|
||||||
err = f.client.Files.Delete(ctx, rootID)
|
|
||||||
return shouldRetry(err)
|
|
||||||
})
|
|
||||||
f.dirCache.ResetRoot()
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy src to this remote using server side copy operations.
|
// Copy src to this remote using server side copy operations.
|
||||||
|
@ -584,16 +584,16 @@ func (f *Fs) Mkdir(ctx context.Context, dir string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rmdir removes the directory or library if empty
|
// purgeCheck removes the root directory, if check is set then it
|
||||||
//
|
// refuses to do so if it has anything in
|
||||||
// Return an error if it doesn't exist or isn't empty
|
func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
|
||||||
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|
||||||
libraryName, dirPath := f.splitPath(dir)
|
libraryName, dirPath := f.splitPath(dir)
|
||||||
libraryID, err := f.getLibraryID(ctx, libraryName)
|
libraryID, err := f.getLibraryID(ctx, libraryName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if check {
|
||||||
directoryEntries, err := f.getDirectoryEntries(ctx, libraryID, dirPath, false)
|
directoryEntries, err := f.getDirectoryEntries(ctx, libraryID, dirPath, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -601,12 +601,21 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
if len(directoryEntries) > 0 {
|
if len(directoryEntries) > 0 {
|
||||||
return fs.ErrorDirectoryNotEmpty
|
return fs.ErrorDirectoryNotEmpty
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if dirPath == "" || dirPath == "/" {
|
if dirPath == "" || dirPath == "/" {
|
||||||
return f.deleteLibrary(ctx, libraryID)
|
return f.deleteLibrary(ctx, libraryID)
|
||||||
}
|
}
|
||||||
return f.deleteDir(ctx, libraryID, dirPath)
|
return f.deleteDir(ctx, libraryID, dirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rmdir removes the directory or library if empty
|
||||||
|
//
|
||||||
|
// Return an error if it doesn't exist or isn't empty
|
||||||
|
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
||||||
|
return f.purgeCheck(ctx, dir, true)
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Optional Interface fs.ListRer ====================
|
// ==================== Optional Interface fs.ListRer ====================
|
||||||
|
|
||||||
// ListR lists the objects and directories of the Fs starting
|
// ListR lists the objects and directories of the Fs starting
|
||||||
@ -893,33 +902,14 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
|
|||||||
|
|
||||||
// ==================== Optional Interface fs.Purger ====================
|
// ==================== Optional Interface fs.Purger ====================
|
||||||
|
|
||||||
// Purge all files in the root and the root directory
|
// Purge all files in the directory
|
||||||
//
|
//
|
||||||
// Implement this if you have a way of deleting all the files
|
// Implement this if you have a way of deleting all the files
|
||||||
// quicker than just running Remove() on the result of List()
|
// quicker than just running Remove() on the result of List()
|
||||||
//
|
//
|
||||||
// Return an error if it doesn't exist
|
// Return an error if it doesn't exist
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
if f.libraryName == "" {
|
return f.purgeCheck(ctx, dir, false)
|
||||||
return errors.New("Cannot delete from the root of the server. Please select a library")
|
|
||||||
}
|
|
||||||
libraryID, err := f.getLibraryID(ctx, f.libraryName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if f.rootDirectory == "" {
|
|
||||||
// Delete library
|
|
||||||
err = f.deleteLibrary(ctx, libraryID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err = f.deleteDir(ctx, libraryID, f.rootDirectory)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Optional Interface fs.CleanUpper ====================
|
// ==================== Optional Interface fs.CleanUpper ====================
|
||||||
|
@ -853,8 +853,8 @@ func (f *Fs) Precision() time.Duration {
|
|||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateItem patches a file or folder
|
// updateItem patches a file or folder
|
||||||
|
@ -895,12 +895,12 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
return dstObj, nil
|
return dstObj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
// Caution: Deleting a folder may orphan objects. It's important
|
// Caution: Deleting a folder may orphan objects. It's important
|
||||||
// to remove the contents of the folder before you delete the
|
// to remove the contents of the folder before you delete the
|
||||||
// folder. That's because removing a folder using DELETE does not
|
// folder. That's because removing a folder using DELETE does not
|
||||||
@ -920,7 +920,7 @@ func (f *Fs) Purge(ctx context.Context) error {
|
|||||||
if f.opt.HardDelete {
|
if f.opt.HardDelete {
|
||||||
return fs.ErrorCantPurge
|
return fs.ErrorCantPurge
|
||||||
}
|
}
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// moveFile moves a file server side
|
// moveFile moves a file server side
|
||||||
|
@ -840,17 +840,21 @@ func (f *Fs) Precision() time.Duration {
|
|||||||
return time.Nanosecond
|
return time.Nanosecond
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and directories
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Implemented here so we can make sure we delete directory markers
|
// Implemented here so we can make sure we delete directory markers
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
|
container, directory := f.split(dir)
|
||||||
|
if container == "" {
|
||||||
|
return fs.ErrorListBucketRequired
|
||||||
|
}
|
||||||
// Delete all the files including the directory markers
|
// Delete all the files including the directory markers
|
||||||
toBeDeleted := make(chan fs.Object, fs.Config.Transfers)
|
toBeDeleted := make(chan fs.Object, fs.Config.Transfers)
|
||||||
delErr := make(chan error, 1)
|
delErr := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
delErr <- operations.DeleteFiles(ctx, toBeDeleted)
|
delErr <- operations.DeleteFiles(ctx, toBeDeleted)
|
||||||
}()
|
}()
|
||||||
err := f.list(f.rootContainer, f.rootDirectory, f.rootDirectory, f.rootContainer == "", true, true, func(entry fs.DirEntry) error {
|
err := f.list(container, directory, f.rootDirectory, false, true, true, func(entry fs.DirEntry) error {
|
||||||
if o, ok := entry.(*Object); ok {
|
if o, ok := entry.(*Object); ok {
|
||||||
toBeDeleted <- o
|
toBeDeleted <- o
|
||||||
}
|
}
|
||||||
@ -864,7 +868,7 @@ func (f *Fs) Purge(ctx context.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return f.Rmdir(ctx, "")
|
return f.Rmdir(ctx, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy src to this remote using server side copy operations.
|
// Copy src to this remote using server side copy operations.
|
||||||
|
@ -162,13 +162,13 @@ func (f *Fs) Mkdir(ctx context.Context, dir string) error {
|
|||||||
return errs.Err()
|
return errs.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge all files in the root and the root directory
|
// Purge all files in the directory
|
||||||
//
|
//
|
||||||
// Implement this if you have a way of deleting all the files
|
// Implement this if you have a way of deleting all the files
|
||||||
// quicker than just running Remove() on the result of List()
|
// quicker than just running Remove() on the result of List()
|
||||||
//
|
//
|
||||||
// Return an error if it doesn't exist
|
// Return an error if it doesn't exist
|
||||||
func (f *Fs) Purge(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
for _, r := range f.upstreams {
|
for _, r := range f.upstreams {
|
||||||
if r.Features().Purge == nil {
|
if r.Features().Purge == nil {
|
||||||
return fs.ErrorCantPurge
|
return fs.ErrorCantPurge
|
||||||
@ -180,7 +180,7 @@ func (f *Fs) Purge(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
errs := Errors(make([]error, len(upstreams)))
|
errs := Errors(make([]error, len(upstreams)))
|
||||||
multithread(len(upstreams), func(i int) {
|
multithread(len(upstreams), func(i int) {
|
||||||
err := upstreams[i].Features().Purge(ctx)
|
err := upstreams[i].Features().Purge(ctx, dir)
|
||||||
errs[i] = errors.Wrap(err, upstreams[i].Name())
|
errs[i] = errors.Wrap(err, upstreams[i].Name())
|
||||||
})
|
})
|
||||||
return errs.Err()
|
return errs.Err()
|
||||||
|
@ -899,13 +899,13 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
|||||||
return f.copyOrMove(ctx, src, remote, "COPY")
|
return f.copyOrMove(ctx, src, remote, "COPY")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move src to this remote using server side move operations.
|
// Move src to this remote using server side move operations.
|
||||||
|
@ -637,13 +637,13 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
|||||||
return f.purgeCheck(ctx, dir, true)
|
return f.purgeCheck(ctx, dir, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Purge deletes all the files and the container
|
// Purge deletes all the files in the directory
|
||||||
//
|
//
|
||||||
// Optional interface: Only implement this if you have a way of
|
// Optional interface: Only implement this if you have a way of
|
||||||
// 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(ctx context.Context) error {
|
func (f *Fs) Purge(ctx context.Context, dir string) error {
|
||||||
return f.purgeCheck(ctx, "", false)
|
return f.purgeCheck(ctx, dir, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyOrMoves copies or moves directories or files depending on the method parameter
|
// copyOrMoves copies or moves directories or files depending on the method parameter
|
||||||
|
8
fs/fs.go
8
fs/fs.go
@ -518,13 +518,13 @@ type Features struct {
|
|||||||
SlowModTime bool // if calling ModTime() generally takes an extra transaction
|
SlowModTime bool // if calling ModTime() generally takes an extra transaction
|
||||||
SlowHash bool // if calling Hash() generally takes an extra transaction
|
SlowHash bool // if calling Hash() generally takes an extra transaction
|
||||||
|
|
||||||
// Purge all files in the root and the root directory
|
// Purge all files in the directory specified
|
||||||
//
|
//
|
||||||
// Implement this if you have a way of deleting all the files
|
// Implement this if you have a way of deleting all the files
|
||||||
// quicker than just running Remove() on the result of List()
|
// quicker than just running Remove() on the result of List()
|
||||||
//
|
//
|
||||||
// Return an error if it doesn't exist
|
// Return an error if it doesn't exist
|
||||||
Purge func(ctx context.Context) error
|
Purge func(ctx context.Context, dir string) error
|
||||||
|
|
||||||
// Copy src to this remote using server side copy operations.
|
// Copy src to this remote using server side copy operations.
|
||||||
//
|
//
|
||||||
@ -883,13 +883,13 @@ func (ft *Features) WrapsFs(f Fs, w Fs) *Features {
|
|||||||
|
|
||||||
// Purger is an optional interfaces for Fs
|
// Purger is an optional interfaces for Fs
|
||||||
type Purger interface {
|
type Purger interface {
|
||||||
// Purge all files in the root and the root directory
|
// Purge all files in the directory specified
|
||||||
//
|
//
|
||||||
// Implement this if you have a way of deleting all the files
|
// Implement this if you have a way of deleting all the files
|
||||||
// quicker than just running Remove() on the result of List()
|
// quicker than just running Remove() on the result of List()
|
||||||
//
|
//
|
||||||
// Return an error if it doesn't exist
|
// Return an error if it doesn't exist
|
||||||
Purge(ctx context.Context) error
|
Purge(ctx context.Context, dir string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copier is an optional interface for Fs
|
// Copier is an optional interface for Fs
|
||||||
|
@ -49,7 +49,7 @@ func TestFeaturesList(t *testing.T) {
|
|||||||
func TestFeaturesEnabled(t *testing.T) {
|
func TestFeaturesEnabled(t *testing.T) {
|
||||||
ft := new(Features)
|
ft := new(Features)
|
||||||
ft.CaseInsensitive = true
|
ft.CaseInsensitive = true
|
||||||
ft.Purge = func(ctx context.Context) error { return nil }
|
ft.Purge = func(ctx context.Context, dir string) error { return nil }
|
||||||
enabled := ft.Enabled()
|
enabled := ft.Enabled()
|
||||||
|
|
||||||
flag, ok := enabled["CaseInsensitive"]
|
flag, ok := enabled["CaseInsensitive"]
|
||||||
|
@ -921,22 +921,18 @@ func Rmdir(ctx context.Context, f fs.Fs, dir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Purge removes a directory and all of its contents
|
// Purge removes a directory and all of its contents
|
||||||
func Purge(ctx context.Context, f fs.Fs, dir string) error {
|
func Purge(ctx context.Context, f fs.Fs, dir string) (err error) {
|
||||||
doFallbackPurge := true
|
doFallbackPurge := true
|
||||||
var err error
|
|
||||||
if dir == "" {
|
|
||||||
// FIXME change the Purge interface so it takes a dir - see #1891
|
|
||||||
if doPurge := f.Features().Purge; doPurge != nil {
|
if doPurge := f.Features().Purge; doPurge != nil {
|
||||||
doFallbackPurge = false
|
doFallbackPurge = false
|
||||||
if SkipDestructive(ctx, fs.LogDirName(f, dir), "purge directory") {
|
if SkipDestructive(ctx, fs.LogDirName(f, dir), "purge directory") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err = doPurge(ctx)
|
err = doPurge(ctx, dir)
|
||||||
if err == fs.ErrorCantPurge {
|
if err == fs.ErrorCantPurge {
|
||||||
doFallbackPurge = true
|
doFallbackPurge = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if doFallbackPurge {
|
if doFallbackPurge {
|
||||||
// DeleteFiles and Rmdir observe --dry-run
|
// DeleteFiles and Rmdir observe --dry-run
|
||||||
err = DeleteFiles(ctx, listToChan(ctx, f, dir))
|
err = DeleteFiles(ctx, listToChan(ctx, f, dir))
|
||||||
|
@ -482,7 +482,7 @@ func Purge(f fs.Fs) {
|
|||||||
if doPurge := f.Features().Purge; doPurge != nil {
|
if doPurge := f.Features().Purge; doPurge != nil {
|
||||||
doFallbackPurge = false
|
doFallbackPurge = false
|
||||||
fs.Debugf(f, "Purge remote")
|
fs.Debugf(f, "Purge remote")
|
||||||
err = doPurge(ctx)
|
err = doPurge(ctx, "")
|
||||||
if err == fs.ErrorCantPurge {
|
if err == fs.ErrorCantPurge {
|
||||||
doFallbackPurge = true
|
doFallbackPurge = true
|
||||||
}
|
}
|
||||||
|
@ -974,6 +974,43 @@ func Run(t *testing.T, opt *Opt) {
|
|||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TestFsPurge tests Purge
|
||||||
|
t.Run("FsPurge", func(t *testing.T) {
|
||||||
|
skipIfNotOk(t)
|
||||||
|
|
||||||
|
// Check have Purge
|
||||||
|
doPurge := remote.Features().Purge
|
||||||
|
if doPurge == nil {
|
||||||
|
t.Skip("FS has no Purge interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
// put up a file to purge
|
||||||
|
fileToPurge := fstest.Item{
|
||||||
|
ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
|
||||||
|
Path: "dirToPurge/fileToPurge.txt",
|
||||||
|
}
|
||||||
|
_, _ = testPut(ctx, t, remote, &fileToPurge)
|
||||||
|
|
||||||
|
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{file1, file2, fileToPurge}, []string{
|
||||||
|
"dirToPurge",
|
||||||
|
"hello? sausage",
|
||||||
|
"hello? sausage/êé",
|
||||||
|
"hello? sausage/êé/Hello, 世界",
|
||||||
|
"hello? sausage/êé/Hello, 世界/ \" ' @ < > & ? + ≠",
|
||||||
|
}, fs.GetModifyWindow(remote))
|
||||||
|
|
||||||
|
// Now purge it
|
||||||
|
err = operations.Purge(ctx, remote, "dirToPurge")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
fstest.CheckListingWithPrecision(t, remote, []fstest.Item{file1, file2}, []string{
|
||||||
|
"hello? sausage",
|
||||||
|
"hello? sausage/êé",
|
||||||
|
"hello? sausage/êé/Hello, 世界",
|
||||||
|
"hello? sausage/êé/Hello, 世界/ \" ' @ < > & ? + ≠",
|
||||||
|
}, fs.GetModifyWindow(remote))
|
||||||
|
})
|
||||||
|
|
||||||
// TestFsCopy tests Copy
|
// TestFsCopy tests Copy
|
||||||
t.Run("FsCopy", func(t *testing.T) {
|
t.Run("FsCopy", func(t *testing.T) {
|
||||||
skipIfNotOk(t)
|
skipIfNotOk(t)
|
||||||
|
Loading…
Reference in New Issue
Block a user