allow cross folder renaming if the underlying resource is the same

this was only allowed for the local filesystem before this change

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino
2022-08-15 21:39:04 +02:00
parent c86db09cd8
commit ced4206c5f
18 changed files with 558 additions and 70 deletions

View File

@@ -104,23 +104,46 @@ func (f *Filesystem) SetNilSecretsIfEmpty() {
}
// IsEqual returns true if the fs is equal to other
func (f *Filesystem) IsEqual(other *Filesystem) bool {
func (f *Filesystem) IsEqual(other Filesystem) bool {
if f.Provider != other.Provider {
return false
}
switch f.Provider {
case sdk.S3FilesystemProvider:
return f.S3Config.isEqual(&other.S3Config)
return f.S3Config.isEqual(other.S3Config)
case sdk.GCSFilesystemProvider:
return f.GCSConfig.isEqual(&other.GCSConfig)
return f.GCSConfig.isEqual(other.GCSConfig)
case sdk.AzureBlobFilesystemProvider:
return f.AzBlobConfig.isEqual(&other.AzBlobConfig)
return f.AzBlobConfig.isEqual(other.AzBlobConfig)
case sdk.CryptedFilesystemProvider:
return f.CryptConfig.isEqual(&other.CryptConfig)
return f.CryptConfig.isEqual(other.CryptConfig)
case sdk.SFTPFilesystemProvider:
return f.SFTPConfig.isEqual(&other.SFTPConfig)
return f.SFTPConfig.isEqual(other.SFTPConfig)
case sdk.HTTPFilesystemProvider:
return f.HTTPConfig.isEqual(&other.HTTPConfig)
return f.HTTPConfig.isEqual(other.HTTPConfig)
default:
return true
}
}
// IsSameResource returns true if fs point to the same resource as other
func (f *Filesystem) IsSameResource(other Filesystem) bool {
if f.Provider != other.Provider {
return false
}
switch f.Provider {
case sdk.S3FilesystemProvider:
return f.S3Config.isSameResource(other.S3Config)
case sdk.GCSFilesystemProvider:
return f.GCSConfig.isSameResource(other.GCSConfig)
case sdk.AzureBlobFilesystemProvider:
return f.AzBlobConfig.isSameResource(other.AzBlobConfig)
case sdk.CryptedFilesystemProvider:
return f.CryptConfig.isSameResource(other.CryptConfig)
case sdk.SFTPFilesystemProvider:
return f.SFTPConfig.isSameResource(other.SFTPConfig)
case sdk.HTTPFilesystemProvider:
return f.HTTPConfig.isSameResource(other.HTTPConfig)
default:
return true
}
@@ -314,6 +337,7 @@ func (f *Filesystem) GetACopy() Filesystem {
Prefix: f.SFTPConfig.Prefix,
DisableCouncurrentReads: f.SFTPConfig.DisableCouncurrentReads,
BufferSize: f.SFTPConfig.BufferSize,
EqualityCheckMode: f.SFTPConfig.EqualityCheckMode,
},
Password: f.SFTPConfig.Password.Clone(),
PrivateKey: f.SFTPConfig.PrivateKey.Clone(),
@@ -321,9 +345,10 @@ func (f *Filesystem) GetACopy() Filesystem {
},
HTTPConfig: HTTPFsConfig{
BaseHTTPFsConfig: sdk.BaseHTTPFsConfig{
Endpoint: f.HTTPConfig.Endpoint,
Username: f.HTTPConfig.Username,
SkipTLSVerify: f.HTTPConfig.SkipTLSVerify,
Endpoint: f.HTTPConfig.Endpoint,
Username: f.HTTPConfig.Username,
SkipTLSVerify: f.HTTPConfig.SkipTLSVerify,
EqualityCheckMode: f.HTTPConfig.EqualityCheckMode,
},
Password: f.HTTPConfig.Password.Clone(),
APIKey: f.HTTPConfig.APIKey.Clone(),

View File

@@ -90,7 +90,7 @@ func (c *HTTPFsConfig) setEmptyCredentialsIfNil() {
}
}
func (c *HTTPFsConfig) isEqual(other *HTTPFsConfig) bool {
func (c *HTTPFsConfig) isEqual(other HTTPFsConfig) bool {
if c.Endpoint != other.Endpoint {
return false
}
@@ -108,6 +108,15 @@ func (c *HTTPFsConfig) isEqual(other *HTTPFsConfig) bool {
return c.APIKey.IsEqual(other.APIKey)
}
func (c *HTTPFsConfig) isSameResource(other HTTPFsConfig) bool {
if c.EqualityCheckMode > 0 || other.EqualityCheckMode > 0 {
if c.Username != other.Username {
return false
}
}
return c.Endpoint == other.Endpoint
}
// validate returns an error if the configuration is not valid
func (c *HTTPFsConfig) validate() error {
c.setEmptyCredentialsIfNil()
@@ -128,6 +137,9 @@ func (c *HTTPFsConfig) validate() error {
return fmt.Errorf("httpfs: invalid unix domain socket path: %q", socketPath)
}
}
if !isEqualityCheckModeValid(c.EqualityCheckMode) {
return errors.New("invalid equality_check_mode")
}
if c.Password.IsEncrypted() && !c.Password.IsValid() {
return errors.New("httpfs: invalid encrypted password")
}
@@ -357,6 +369,9 @@ func (fs *HTTPFs) Create(name string, flag int) (File, *PipeWriter, func(), erro
// Rename renames (moves) source to target.
func (fs *HTTPFs) Rename(source, target string) error {
if source == target {
return nil
}
ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
defer cancelFn()

View File

@@ -116,6 +116,9 @@ func (*OsFs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
// Rename renames (moves) source to target
func (fs *OsFs) Rename(source, target string) error {
if source == target {
return nil
}
err := os.Rename(source, target)
if err != nil && isCrossDeviceError(err) {
fsLog(fs, logger.LevelError, "cross device error detected while renaming %#v -> %#v. Trying a copy and remove, this could take a long time",

View File

@@ -83,7 +83,7 @@ func (c *SFTPFsConfig) setNilSecretsIfEmpty() {
}
}
func (c *SFTPFsConfig) isEqual(other *SFTPFsConfig) bool {
func (c *SFTPFsConfig) isEqual(other SFTPFsConfig) bool {
if c.Endpoint != other.Endpoint {
return false
}
@@ -130,6 +130,15 @@ func (c *SFTPFsConfig) setEmptyCredentialsIfNil() {
}
}
func (c *SFTPFsConfig) isSameResource(other SFTPFsConfig) bool {
if c.EqualityCheckMode > 0 || other.EqualityCheckMode > 0 {
if c.Username != other.Username {
return false
}
}
return c.Endpoint == other.Endpoint
}
// validate returns an error if the configuration is not valid
func (c *SFTPFsConfig) validate() error {
c.setEmptyCredentialsIfNil()
@@ -146,6 +155,9 @@ func (c *SFTPFsConfig) validate() error {
if c.BufferSize < 0 || c.BufferSize > 16 {
return errors.New("invalid buffer_size, valid range is 0-16")
}
if !isEqualityCheckModeValid(c.EqualityCheckMode) {
return errors.New("invalid equality_check_mode")
}
if err := c.validateCredentials(); err != nil {
return err
}
@@ -378,6 +390,9 @@ func (fs *SFTPFs) Create(name string, flag int) (File, *PipeWriter, func(), erro
// Rename renames (moves) source to target.
func (fs *SFTPFs) Rename(source, target string) error {
if source == target {
return nil
}
if err := fs.checkConnection(); err != nil {
return err
}

View File

@@ -169,7 +169,7 @@ func (c *S3FsConfig) HideConfidentialData() {
}
}
func (c *S3FsConfig) isEqual(other *S3FsConfig) bool {
func (c *S3FsConfig) isEqual(other S3FsConfig) bool {
if c.Bucket != other.Bucket {
return false
}
@@ -204,7 +204,7 @@ func (c *S3FsConfig) isEqual(other *S3FsConfig) bool {
return c.isSecretEqual(other)
}
func (c *S3FsConfig) areMultipartFieldsEqual(other *S3FsConfig) bool {
func (c *S3FsConfig) areMultipartFieldsEqual(other S3FsConfig) bool {
if c.UploadPartSize != other.UploadPartSize {
return false
}
@@ -226,7 +226,7 @@ func (c *S3FsConfig) areMultipartFieldsEqual(other *S3FsConfig) bool {
return true
}
func (c *S3FsConfig) isSecretEqual(other *S3FsConfig) bool {
func (c *S3FsConfig) isSecretEqual(other S3FsConfig) bool {
if c.AccessSecret == nil {
c.AccessSecret = kms.NewEmptySecret()
}
@@ -283,6 +283,16 @@ func (c *S3FsConfig) checkPartSizeAndConcurrency() error {
return nil
}
func (c *S3FsConfig) isSameResource(other S3FsConfig) bool {
if c.Bucket != other.Bucket {
return false
}
if c.Endpoint != other.Endpoint {
return false
}
return c.Region == other.Region
}
// validate returns an error if the configuration is not valid
func (c *S3FsConfig) validate() error {
if c.AccessSecret == nil {
@@ -341,7 +351,7 @@ func (c *GCSFsConfig) ValidateAndEncryptCredentials(additionalData string) error
return nil
}
func (c *GCSFsConfig) isEqual(other *GCSFsConfig) bool {
func (c *GCSFsConfig) isEqual(other GCSFsConfig) bool {
if c.Bucket != other.Bucket {
return false
}
@@ -366,6 +376,10 @@ func (c *GCSFsConfig) isEqual(other *GCSFsConfig) bool {
return c.Credentials.IsEqual(other.Credentials)
}
func (c *GCSFsConfig) isSameResource(other GCSFsConfig) bool {
return c.Bucket == other.Bucket
}
// validate returns an error if the configuration is not valid
func (c *GCSFsConfig) validate() error {
if c.Credentials == nil || c.AutomaticCredentials == 1 {
@@ -414,7 +428,7 @@ func (c *AzBlobFsConfig) HideConfidentialData() {
}
}
func (c *AzBlobFsConfig) isEqual(other *AzBlobFsConfig) bool {
func (c *AzBlobFsConfig) isEqual(other AzBlobFsConfig) bool {
if c.Container != other.Container {
return false
}
@@ -457,7 +471,7 @@ func (c *AzBlobFsConfig) isEqual(other *AzBlobFsConfig) bool {
return c.isSecretEqual(other)
}
func (c *AzBlobFsConfig) isSecretEqual(other *AzBlobFsConfig) bool {
func (c *AzBlobFsConfig) isSecretEqual(other AzBlobFsConfig) bool {
if c.AccountKey == nil {
c.AccountKey = kms.NewEmptySecret()
}
@@ -533,6 +547,16 @@ func (c *AzBlobFsConfig) tryDecrypt() error {
return nil
}
func (c *AzBlobFsConfig) isSameResource(other AzBlobFsConfig) bool {
if c.AccountName != other.AccountName {
return false
}
if c.Endpoint != other.Endpoint {
return false
}
return c.SASURL.GetPayload() == other.SASURL.GetPayload()
}
// validate returns an error if the configuration is not valid
func (c *AzBlobFsConfig) validate() error {
if c.AccountKey == nil {
@@ -578,7 +602,7 @@ func (c *CryptFsConfig) HideConfidentialData() {
}
}
func (c *CryptFsConfig) isEqual(other *CryptFsConfig) bool {
func (c *CryptFsConfig) isEqual(other CryptFsConfig) bool {
if c.Passphrase == nil {
c.Passphrase = kms.NewEmptySecret()
}
@@ -602,6 +626,10 @@ func (c *CryptFsConfig) ValidateAndEncryptCredentials(additionalData string) err
return nil
}
func (c *CryptFsConfig) isSameResource(other CryptFsConfig) bool {
return c.Passphrase.GetPayload() == other.Passphrase.GetPayload()
}
// validate returns an error if the configuration is not valid
func (c *CryptFsConfig) validate() error {
if c.Passphrase == nil || c.Passphrase.IsEmpty() {
@@ -656,6 +684,10 @@ func (p *PipeWriter) Write(data []byte) (int, error) {
return p.writer.Write(data)
}
func isEqualityCheckModeValid(mode int) bool {
return mode >= 0 || mode <= 1
}
// IsDirectory checks if a path exists and is a directory
func IsDirectory(fs Fs, path string) (bool, error) {
fileInfo, err := fs.Stat(path)