From 20ae7d562b839b4aabd7107531dc0e2739ad2bc5 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 9 Aug 2017 15:27:43 +0100 Subject: [PATCH] fs: Add CanHaveEmptyDirectories and BucketBased feature flags to all remotes --- amazonclouddrive/amazonclouddrive.go | 6 +++++- azureblob/azureblob.go | 6 +++++- b2/b2.go | 6 +++++- box/box.go | 5 ++++- crypt/crypt.go | 10 ++++++---- drive/drive.go | 7 ++++++- dropbox/dropbox.go | 6 +++++- fs/fs.go | 14 +++++++++----- ftp/ftp.go | 4 +++- googlecloudstorage/googlecloudstorage.go | 6 +++++- http/http.go | 4 +++- local/local.go | 5 ++++- onedrive/onedrive.go | 6 +++++- qingstor/qingstor.go | 6 +++++- s3/s3.go | 6 +++++- sftp/sftp.go | 4 +++- swift/swift.go | 6 +++++- yandex/yandex.go | 6 +++++- 18 files changed, 88 insertions(+), 25 deletions(-) diff --git a/amazonclouddrive/amazonclouddrive.go b/amazonclouddrive/amazonclouddrive.go index ca7f325a8..d6ab93123 100644 --- a/amazonclouddrive/amazonclouddrive.go +++ b/amazonclouddrive/amazonclouddrive.go @@ -190,7 +190,11 @@ func NewFs(name, root string) (fs.Fs, error) { pacer: pacer.New().SetMinSleep(minSleep).SetPacer(pacer.AmazonCloudDrivePacer), noAuthClient: fs.Config.Client(), } - f.features = (&fs.Features{CaseInsensitive: true, ReadMimeType: true}).Fill(f) + f.features = (&fs.Features{ + CaseInsensitive: true, + ReadMimeType: true, + CanHaveEmptyDirectories: true, + }).Fill(f) // Renew the token in the background f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error { diff --git a/azureblob/azureblob.go b/azureblob/azureblob.go index 5a303b5e0..7062602b4 100644 --- a/azureblob/azureblob.go +++ b/azureblob/azureblob.go @@ -214,7 +214,11 @@ func NewFs(name, root string) (fs.Fs, error) { pacer: pacer.New().SetMinSleep(minSleep).SetMaxSleep(maxSleep).SetDecayConstant(decayConstant), uploadToken: pacer.NewTokenDispenser(fs.Config.Transfers), } - f.features = (&fs.Features{ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + ReadMimeType: true, + WriteMimeType: true, + BucketBased: true, + }).Fill(f) if f.root != "" { f.root += "/" // Check to see if the (container,directory) is actually an existing file diff --git a/b2/b2.go b/b2/b2.go index f0ef9a414..0ee1cd233 100644 --- a/b2/b2.go +++ b/b2/b2.go @@ -257,7 +257,11 @@ func NewFs(name, root string) (fs.Fs, error) { pacer: pacer.New().SetMinSleep(minSleep).SetMaxSleep(maxSleep).SetDecayConstant(decayConstant), bufferTokens: make(chan []byte, fs.Config.Transfers), } - f.features = (&fs.Features{ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + ReadMimeType: true, + WriteMimeType: true, + BucketBased: true, + }).Fill(f) // Set the test flag if required if *b2TestMode != "" { testMode := strings.TrimSpace(*b2TestMode) diff --git a/box/box.go b/box/box.go index 0fb076468..7b06325f3 100644 --- a/box/box.go +++ b/box/box.go @@ -244,7 +244,10 @@ func NewFs(name, root string) (fs.Fs, error) { pacer: pacer.New().SetMinSleep(minSleep).SetMaxSleep(maxSleep).SetDecayConstant(decayConstant), uploadToken: pacer.NewTokenDispenser(fs.Config.Transfers), } - f.features = (&fs.Features{CaseInsensitive: true}).Fill(f) + f.features = (&fs.Features{ + CaseInsensitive: true, + CanHaveEmptyDirectories: true, + }).Fill(f) f.srv.SetErrorHandler(errorHandler) // Renew the token in the background diff --git a/crypt/crypt.go b/crypt/crypt.go index a271a38c2..f779fbc97 100644 --- a/crypt/crypt.go +++ b/crypt/crypt.go @@ -104,10 +104,12 @@ func NewFs(name, rpath string) (fs.Fs, error) { // the features here are ones we could support, and they are // ANDed with the ones from wrappedFs f.features = (&fs.Features{ - CaseInsensitive: mode == NameEncryptionOff, - DuplicateFiles: true, - ReadMimeType: false, // MimeTypes not supported with crypt - WriteMimeType: false, + CaseInsensitive: mode == NameEncryptionOff, + DuplicateFiles: true, + ReadMimeType: false, // MimeTypes not supported with crypt + WriteMimeType: false, + BucketBased: true, + CanHaveEmptyDirectories: true, }).Fill(f).Mask(wrappedFs) return f, err } diff --git a/drive/drive.go b/drive/drive.go index 745a60668..f0339e241 100644 --- a/drive/drive.go +++ b/drive/drive.go @@ -408,7 +408,12 @@ func NewFs(name, path string) (fs.Fs, error) { } f.teamDriveID = fs.ConfigFileGet(name, "team_drive") f.isTeamDrive = f.teamDriveID != "" - f.features = (&fs.Features{DuplicateFiles: true, ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + DuplicateFiles: true, + ReadMimeType: true, + WriteMimeType: true, + CanHaveEmptyDirectories: true, + }).Fill(f) // Create a new authorized Drive client. f.client = oAuthClient diff --git a/dropbox/dropbox.go b/dropbox/dropbox.go index d038139c1..7d787e0f4 100644 --- a/dropbox/dropbox.go +++ b/dropbox/dropbox.go @@ -185,7 +185,11 @@ func NewFs(name, root string) (fs.Fs, error) { srv: srv, pacer: pacer.New().SetMinSleep(minSleep).SetMaxSleep(maxSleep).SetDecayConstant(decayConstant), } - f.features = (&fs.Features{CaseInsensitive: true, ReadMimeType: true}).Fill(f) + f.features = (&fs.Features{ + CaseInsensitive: true, + ReadMimeType: true, + CanHaveEmptyDirectories: true, + }).Fill(f) f.setRoot(root) // See if the root is actually an object diff --git a/fs/fs.go b/fs/fs.go index 826d08e12..6a85a5c71 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -245,11 +245,13 @@ type ListRFn func(dir string, callback ListRCallback) error // Features describe the optional features of the Fs type Features struct { - // Feature flags - CaseInsensitive bool - DuplicateFiles bool - ReadMimeType bool - WriteMimeType bool + // Feature flags, whether Fs + CaseInsensitive bool // has case insensitive files + DuplicateFiles bool // allows duplicate files + ReadMimeType bool // can read the mime type of objects + WriteMimeType bool // can set the mime type of objects + CanHaveEmptyDirectories bool // can have empty directories + BucketBased bool // is bucket based (like s3, swift etc) // Purge all files in the root and the root directory // @@ -444,6 +446,8 @@ func (ft *Features) Mask(f Fs) *Features { ft.DuplicateFiles = ft.DuplicateFiles && mask.DuplicateFiles ft.ReadMimeType = ft.ReadMimeType && mask.ReadMimeType ft.WriteMimeType = ft.WriteMimeType && mask.WriteMimeType + ft.CanHaveEmptyDirectories = ft.CanHaveEmptyDirectories && mask.CanHaveEmptyDirectories + ft.BucketBased = ft.BucketBased && mask.BucketBased if mask.Purge == nil { ft.Purge = nil } diff --git a/ftp/ftp.go b/ftp/ftp.go index f4ba8ef2d..b3b304265 100644 --- a/ftp/ftp.go +++ b/ftp/ftp.go @@ -207,7 +207,9 @@ func NewFs(name, root string) (ff fs.Fs, err error) { pass: pass, dialAddr: dialAddr, } - f.features = (&fs.Features{}).Fill(f) + f.features = (&fs.Features{ + CanHaveEmptyDirectories: true, + }).Fill(f) // Make a connection and pool it to return errors early c, err := f.getFtpConnection() if err != nil { diff --git a/googlecloudstorage/googlecloudstorage.go b/googlecloudstorage/googlecloudstorage.go index 0bce47173..40ceaf670 100644 --- a/googlecloudstorage/googlecloudstorage.go +++ b/googlecloudstorage/googlecloudstorage.go @@ -317,7 +317,11 @@ func NewFs(name, root string) (fs.Fs, error) { location: fs.ConfigFileGet(name, "location"), storageClass: fs.ConfigFileGet(name, "storage_class"), } - f.features = (&fs.Features{ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + ReadMimeType: true, + WriteMimeType: true, + BucketBased: true, + }).Fill(f) if f.objectACL == "" { f.objectACL = "private" } diff --git a/http/http.go b/http/http.go index a6df9834a..13fc21851 100644 --- a/http/http.go +++ b/http/http.go @@ -151,7 +151,9 @@ func NewFs(name, root string) (fs.Fs, error) { endpoint: u, endpointURL: u.String(), } - f.features = (&fs.Features{}).Fill(f) + f.features = (&fs.Features{ + CanHaveEmptyDirectories: true, + }).Fill(f) if isFile { return f, fs.ErrorIsFile } diff --git a/local/local.go b/local/local.go index 965079673..ae4d477c1 100644 --- a/local/local.go +++ b/local/local.go @@ -92,7 +92,10 @@ func NewFs(name, root string) (fs.Fs, error) { dirNames: newMapper(), } f.root = f.cleanPath(root) - f.features = (&fs.Features{CaseInsensitive: f.caseInsensitive()}).Fill(f) + f.features = (&fs.Features{ + CaseInsensitive: f.caseInsensitive(), + CanHaveEmptyDirectories: true, + }).Fill(f) if *followSymlinks { f.lstat = os.Stat } diff --git a/onedrive/onedrive.go b/onedrive/onedrive.go index 3616edcea..8379693ab 100644 --- a/onedrive/onedrive.go +++ b/onedrive/onedrive.go @@ -205,7 +205,11 @@ func NewFs(name, root string) (fs.Fs, error) { srv: rest.NewClient(oAuthClient).SetRoot(rootURL), pacer: pacer.New().SetMinSleep(minSleep).SetMaxSleep(maxSleep).SetDecayConstant(decayConstant), } - f.features = (&fs.Features{CaseInsensitive: true, ReadMimeType: true}).Fill(f) + f.features = (&fs.Features{ + CaseInsensitive: true, + ReadMimeType: true, + CanHaveEmptyDirectories: true, + }).Fill(f) f.srv.SetErrorHandler(errorHandler) // Renew the token in the background diff --git a/qingstor/qingstor.go b/qingstor/qingstor.go index 4555fa044..96f0b4178 100644 --- a/qingstor/qingstor.go +++ b/qingstor/qingstor.go @@ -240,7 +240,11 @@ func NewFs(name, root string) (fs.Fs, error) { bucket: bucket, svc: svc, } - f.features = (&fs.Features{ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + ReadMimeType: true, + WriteMimeType: true, + BucketBased: true, + }).Fill(f) if f.root != "" { if !strings.HasSuffix(f.root, "/") { diff --git a/s3/s3.go b/s3/s3.go index 9d322c7ae..0fcefb04a 100644 --- a/s3/s3.go +++ b/s3/s3.go @@ -401,7 +401,11 @@ func NewFs(name, root string) (fs.Fs, error) { sse: fs.ConfigFileGet(name, "server_side_encryption"), storageClass: fs.ConfigFileGet(name, "storage_class"), } - f.features = (&fs.Features{ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + ReadMimeType: true, + WriteMimeType: true, + BucketBased: true, + }).Fill(f) if *s3ACL != "" { f.acl = *s3ACL } diff --git a/sftp/sftp.go b/sftp/sftp.go index 3f65aea97..ff7d0ee63 100644 --- a/sftp/sftp.go +++ b/sftp/sftp.go @@ -290,7 +290,9 @@ func NewFs(name, root string) (fs.Fs, error) { mkdirLock: newStringLock(), connLimit: rate.NewLimiter(rate.Limit(connectionsPerSecond), 1), } - f.features = (&fs.Features{}).Fill(f) + f.features = (&fs.Features{ + CanHaveEmptyDirectories: true, + }).Fill(f) // Make a connection and pool it to return errors early c, err := f.getSftpConnection() if err != nil { diff --git a/swift/swift.go b/swift/swift.go index 5118b9aeb..489402573 100644 --- a/swift/swift.go +++ b/swift/swift.go @@ -200,7 +200,11 @@ func NewFsWithConnection(name, root string, c *swift.Connection) (fs.Fs, error) segmentsContainer: container + "_segments", root: directory, } - f.features = (&fs.Features{ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + ReadMimeType: true, + WriteMimeType: true, + BucketBased: true, + }).Fill(f) // StorageURL overloading storageURL := fs.ConfigFileGet(name, "storage_url") if storageURL != "" { diff --git a/yandex/yandex.go b/yandex/yandex.go index b923e031f..5a08ae2ea 100644 --- a/yandex/yandex.go +++ b/yandex/yandex.go @@ -133,7 +133,11 @@ func NewFs(name, root string) (fs.Fs, error) { name: name, yd: yandexDisk, } - f.features = (&fs.Features{ReadMimeType: true, WriteMimeType: true}).Fill(f) + f.features = (&fs.Features{ + ReadMimeType: true, + WriteMimeType: true, + CanHaveEmptyDirectories: true, + }).Fill(f) f.setRoot(root) // Check to see if the object exists and is a file