diff --git a/backend/swift/swift.go b/backend/swift/swift.go index d207ef68f..9dea6dd1d 100644 --- a/backend/swift/swift.go +++ b/backend/swift/swift.go @@ -17,6 +17,7 @@ import ( "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/configstruct" + "github.com/rclone/rclone/fs/encodings" "github.com/rclone/rclone/fs/fserrors" "github.com/rclone/rclone/fs/fshttp" "github.com/rclone/rclone/fs/hash" @@ -60,6 +61,8 @@ copy operations.`, Advanced: true, }} +const enc = encodings.Swift + // Register with Fs func init() { fs.Register(&fs.RegInfo{ @@ -320,7 +323,8 @@ func parsePath(path string) (root string) { // split returns container and containerPath from the rootRelativePath // relative to f.root func (f *Fs) split(rootRelativePath string) (container, containerPath string) { - return bucket.Split(path.Join(f.root, rootRelativePath)) + container, containerPath = bucket.Split(path.Join(f.root, rootRelativePath)) + return enc.FromStandardName(container), enc.FromStandardPath(containerPath) } // split returns container and containerPath from the object @@ -441,9 +445,10 @@ func NewFsWithConnection(opt *Options, name, root string, c *swift.Connection, n // Check to see if the object exists - ignoring directory markers var info swift.Object var err error + encodedDirectory := enc.FromStandardPath(f.rootDirectory) err = f.pacer.Call(func() (bool, error) { var rxHeaders swift.Headers - info, rxHeaders, err = f.c.Object(f.rootContainer, f.rootDirectory) + info, rxHeaders, err = f.c.Object(f.rootContainer, encodedDirectory) return shouldRetryHeaders(rxHeaders, err) }) if err == nil && info.ContentType != directoryMarkerContentType { @@ -553,17 +558,18 @@ func (f *Fs) listContainerRoot(container, directory, prefix string, addContainer if !recurse { isDirectory = strings.HasSuffix(object.Name, "/") } - if !strings.HasPrefix(object.Name, prefix) { - fs.Logf(f, "Odd name received %q", object.Name) + remote := enc.ToStandardPath(object.Name) + if !strings.HasPrefix(remote, prefix) { + fs.Logf(f, "Odd name received %q", remote) continue } - if object.Name == prefix { + if remote == prefix { // If we have zero length directory markers ending in / then swift // will return them in the listing for the directory which causes // duplicate directories. Ignore them here. continue } - remote := object.Name[len(prefix):] + remote = remote[len(prefix):] if addContainer { remote = path.Join(container, remote) } @@ -635,7 +641,7 @@ func (f *Fs) listContainers(ctx context.Context) (entries fs.DirEntries, err err } for _, container := range containers { f.cache.MarkOK(container.Name) - d := fs.NewDir(container.Name, time.Time{}).SetSize(container.Bytes).SetItems(container.Count) + d := fs.NewDir(enc.ToStandardName(container.Name), time.Time{}).SetSize(container.Bytes).SetItems(container.Count) entries = append(entries, d) } return entries, nil diff --git a/docs/content/swift.md b/docs/content/swift.md index 3c5750a0d..39c1e8023 100644 --- a/docs/content/swift.md +++ b/docs/content/swift.md @@ -472,6 +472,16 @@ ns. This is a defacto standard (used in the official python-swiftclient amongst others) for storing the modification time for an object. +### Restricted filename characters + +| Character | Value | Replacement | +| --------- |:-----:|:-----------:| +| NUL | 0x00 | ␀ | +| / | 0x2F | / | + +Invalid UTF-8 bytes will also be [replaced](/overview/#invalid-utf8), +as they can't be used in JSON strings. + ### Limitations ### The Swift API doesn't return a correct MD5SUM for segmented files diff --git a/fs/encodings/encodings.go b/fs/encodings/encodings.go index 8b9ea5b6e..04bb6c3f5 100644 --- a/fs/encodings/encodings.go +++ b/fs/encodings/encodings.go @@ -257,6 +257,12 @@ const S3 = encoder.MultiEncoder( encoder.EncodeSlash | encoder.EncodeDot) +// Swift is the encoding used by the swift backend +const Swift = encoder.MultiEncoder( + encoder.EncodeInvalidUtf8 | + encoder.EncodeSlash) + + // ByName returns the encoder for a give backend name or nil func ByName(name string) encoder.Encoder { switch strings.ToLower(name) { @@ -302,9 +308,11 @@ func ByName(name string) encoder.Encoder { case "pcloud": return Pcloud //case "qingstor": - //case "s3": + case "s3": + return S3 //case "sftp": - //case "swift": + case "swift": + return Swift //case "webdav": //case "yandex": default: