mirror of
https://github.com/rclone/rclone.git
synced 2025-01-05 13:59:25 +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
backend
amazonclouddrive
azureblob
b2
box
cache
chunker
crypt
drive
dropbox
jottacloud
local
mailru
mega
onedrive
opendrive
pcloud
premiumizeme
putio
seafile
sharefile
sugarsync
swift
union
webdav
yandex
fs
fstest
@ -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
|
||||||
|
15
backend/cache/cache.go
vendored
15
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 {
|
||||||
fs.Infof(f, "purging cache")
|
if dir == "" {
|
||||||
f.cache.Purge()
|
// FIXME this isn't quite right as it should purge the dir prefix
|
||||||
|
fs.Infof(f, "purging cache")
|
||||||
|
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,20 +2223,22 @@ 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
|
||||||
found, err := f.list(ctx, []string{directoryID}, "", false, false, true, func(item *drive.File) bool {
|
if check {
|
||||||
if !item.Trashed {
|
found, err := f.list(ctx, []string{directoryID}, "", false, false, true, func(item *drive.File) bool {
|
||||||
fs.Debugf(dir, "Rmdir: contains file: %q", item.Name)
|
if !item.Trashed {
|
||||||
return true
|
fs.Debugf(dir, "Rmdir: contains file: %q", item.Name)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
fs.Debugf(dir, "Rmdir: contains trashed file: %q", item.Name)
|
||||||
|
trashedFiles = true
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
return errors.Errorf("directory not empty")
|
||||||
}
|
}
|
||||||
fs.Debugf(dir, "Rmdir: contains trashed file: %q", item.Name)
|
|
||||||
trashedFiles = true
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if found {
|
|
||||||
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
|
||||||
@ -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,31 +621,33 @@ 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check directory exists
|
if check {
|
||||||
_, err := f.getDirMetadata(root)
|
// check directory exists
|
||||||
if err != nil {
|
_, err = f.getDirMetadata(root)
|
||||||
return errors.Wrap(err, "Rmdir")
|
if err != nil {
|
||||||
}
|
return errors.Wrap(err, "Rmdir")
|
||||||
|
}
|
||||||
|
|
||||||
root = f.opt.Enc.FromStandardPath(root)
|
root = f.opt.Enc.FromStandardPath(root)
|
||||||
// check directory empty
|
// check directory empty
|
||||||
arg := files.ListFolderArg{
|
arg := files.ListFolderArg{
|
||||||
Path: root,
|
Path: root,
|
||||||
Recursive: false,
|
Recursive: false,
|
||||||
}
|
}
|
||||||
if root == "/" {
|
if root == "/" {
|
||||||
arg.Path = "" // Specify root folder as empty string
|
arg.Path = "" // Specify root folder as empty string
|
||||||
}
|
}
|
||||||
var res *files.ListFolderResult
|
var res *files.ListFolderResult
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
res, err = f.srv.ListFolder(&arg)
|
res, err = f.srv.ListFolder(&arg)
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Rmdir")
|
return errors.Wrap(err, "Rmdir")
|
||||||
}
|
}
|
||||||
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
|
||||||
@ -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,18 +477,20 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) (err error) {
|
|||||||
}
|
}
|
||||||
dirID := atoi(directoryID)
|
dirID := atoi(directoryID)
|
||||||
|
|
||||||
// check directory empty
|
if check {
|
||||||
var children []putio.File
|
// check directory empty
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
var children []putio.File
|
||||||
// fs.Debugf(f, "listing files: %d", dirID)
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
children, _, err = f.client.Files.List(ctx, dirID)
|
// fs.Debugf(f, "listing files: %d", dirID)
|
||||||
return shouldRetry(err)
|
children, _, err = f.client.Files.List(ctx, dirID)
|
||||||
})
|
return shouldRetry(err)
|
||||||
if err != nil {
|
})
|
||||||
return errors.Wrap(err, "Rmdir")
|
if err != nil {
|
||||||
}
|
return errors.Wrap(err, "Rmdir")
|
||||||
if len(children) != 0 {
|
}
|
||||||
return errors.New("directory not empty")
|
if len(children) != 0 {
|
||||||
|
return errors.New("directory not empty")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove it
|
// remove it
|
||||||
@ -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,29 +584,38 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryEntries, err := f.getDirectoryEntries(ctx, libraryID, dirPath, false)
|
if check {
|
||||||
if err != nil {
|
directoryEntries, err := f.getDirectoryEntries(ctx, libraryID, dirPath, false)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
if len(directoryEntries) > 0 {
|
}
|
||||||
return fs.ErrorDirectoryNotEmpty
|
if len(directoryEntries) > 0 {
|
||||||
|
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,20 +921,16 @@ 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 doPurge := f.Features().Purge; doPurge != nil {
|
||||||
if dir == "" {
|
doFallbackPurge = false
|
||||||
// FIXME change the Purge interface so it takes a dir - see #1891
|
if SkipDestructive(ctx, fs.LogDirName(f, dir), "purge directory") {
|
||||||
if doPurge := f.Features().Purge; doPurge != nil {
|
return nil
|
||||||
doFallbackPurge = false
|
}
|
||||||
if SkipDestructive(ctx, fs.LogDirName(f, dir), "purge directory") {
|
err = doPurge(ctx, dir)
|
||||||
return nil
|
if err == fs.ErrorCantPurge {
|
||||||
}
|
doFallbackPurge = true
|
||||||
err = doPurge(ctx)
|
|
||||||
if err == fs.ErrorCantPurge {
|
|
||||||
doFallbackPurge = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if doFallbackPurge {
|
if doFallbackPurge {
|
||||||
|
@ -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